IOS之數據持久化


9.1 數據持久化概述

9.2 iOS應用程序目錄結構

9.3 讀寫屬性列表

9.4 對象歸檔

9.5 訪問SQLite

9.1 數據持久化概述

iOS中可以有四種持久化數據的方式: 屬性列表、對象歸檔、SQLite3和Core Data

9.2 iOS應用程序目錄結構

iOS應用程序運行在Mac os模擬器時候,有一下臨時目錄模擬器3.1.3為例子:

/Users/tony/Library/Application Support/iPhone Simulator/3.1.3/Applications

wps_clip_image-26293

IOS應用程序采用沙盒原理設計,ios每個應用程序都有自己的3個目錄(Document,Library,tmp),互相之間不能訪問。

Documents存放應用程序的數據。

Library目錄下面還有Preferences和Caches目錄,Preferences目錄存放應用程序的使用偏好,Caches目錄與Documents很相 似可以存放應用程序的數據。

tmp目錄供應用程序存儲臨時文件。

9.3 讀寫屬性列表

讀取Documents目錄下文件

可以獲得應用程序的Documents文件夾。

    NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* myDocPath = [myPaths objectAtIndex:0];

獲取文件的完整路徑。

- (NSString*)filePath:(NSString*)fileName {
    NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* myDocPath = [myPaths objectAtIndex:0];
    NSString* filePath = [myDocPath stringByAppendingPathComponent:fileName];
    return filePath;
}

 

獲取tmp目錄

獲取應用程序的tmp目錄要比獲取Documents目錄容易的多。使用函數NSTemporaryDirectory ()可以獲得tmp目錄路徑。

NSString* tempPath = NSTemporaryDirectory();

獲取文件的完整路徑。

NSString* tempFile = [tempPath stringByAppendingPathComponent:@"properties.plist"];

 

屬性列表文件實例 :PropertesList

wps_clip_image-4791

PropertesListViewController.h

#import "Student.h"

@interface ViewController : UIViewController

@property (retain, nonatomic) IBOutlet UITextField *studentNo;
@property (retain, nonatomic) IBOutlet UITextField *studentName;
@property (retain, nonatomic) IBOutlet UITextField *studentClass;
- (IBAction)save:(id)sender;
- (IBAction)load:(id)sender;
- (IBAction)endEditing:(id)sender;
- (IBAction)saveToArchiver:(id)sender;
- (IBAction)loadFromArchiver:(id)sender;
- (NSString*)filePath:(NSString*)fileName;
@end

 

PropertesListViewController.m

@synthesize studentNo;
@synthesize studentName;
@synthesize studentClass;

- (NSString*)filePath:(NSString*)fileName {
    NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* myDocPath = [myPaths objectAtIndex:0];
    NSString* filePath = [myDocPath stringByAppendingPathComponent:fileName];
    return filePath;
}

- (IBAction)save:(id)sender {

    NSString* fileName = [self filePath:@"properties.plist"];
    NSLog(fileName);
    NSMutableArray* data = [[NSMutableArray alloc]init];

    [data addObject:studentNo.text];
    [data addObject:studentName.text];
    [data addObject:studentClass.text];
    [data writeToFile:fileName atomically:YES];
}

- (IBAction)load:(id)sender {
    NSString* fileName = [self filePath:@"properties.plist"];
    if ([[NSFileManager defaultManager]fileExistsAtPath:fileName]) {
        NSArray* data = [[NSArray alloc]initWithContentsOfFile:fileName];
        studentNo.text = [data objectAtIndex:0];
        studentName.text = [data objectAtIndex:1];
        studentClass.text = [data objectAtIndex:2];
        [data release];
    }
    
}

- (IBAction)endEditing:(id)sender {
    [sender resignFirstResponder];
}

wps_clip_image-8352

wps_clip_image-11973

9.4 對象歸檔

對象歸檔實例:Encoding

對象歸檔

“歸檔”是值另一種形式的序列化,對模型對象進行歸檔的技術可以輕松將復雜的對象寫入文件,然后再從中讀取它們,只要在類中實現的每個屬性都是基本數據類型(如int或float)或都是符合NSCoding協議的某個類的實例,你就可以對你的對象進行完整歸檔。

實現NSCoding協議

NSCoding協議聲明了兩個方法: -(void)encodeWithCoder:(NSCoder *)aCoder,是將對象寫入到文件中。 

-(id)initWithCoder:(NSCoder *)aDecoder,是將文件中數據讀入到對象中。

實現NSCopying協議

NSCopying協議聲明了一個方法: -(id)copyWithZone:(NSZone *)zone ,是將對象復制方法。 

Student.h

@interface Student : NSObject<NSCoding, NSCopying>

@property (retain, nonatomic) NSString* studentNo;
@property (retain, nonatomic) NSString* studentName;
@property (retain, nonatomic) NSString* studentClass;

@end

 

Student.m

#import "Student.h"

@implementation Student

@synthesize studentNo = _studentNo;
@synthesize studentName = _studentName;
@synthesize studentClass = _studentClass;

#pragma mark NSCopying

- (id)copyWithZone:(NSZone *)zone {
    Student* copy = [[[self class]allocWithZone:zone]init];
    copy.studentNo = [_studentNo copyWithZone:zone];
    copy.studentName = [_studentName copyWithZone:zone];
    copy.studentClass = [_studentClass copyWithZone:zone];
    return copy;
}

#pragma mark  NSCoding

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_studentNo forKey:@"studentNo"];
    [aCoder encodeObject:_studentName forKey:@"studentName"];
    [aCoder encodeObject:_studentClass forKey:@"studentClass"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
    _studentNo = [aDecoder decodeObjectForKey:@"studentNo"];
    _studentName = [aDecoder decodeObjectForKey:@"studentName"];
    _studentClass = [aDecoder decodeObjectForKey:@"studentClass"];
    return self;
}
-(NSString*)description {
    return [[[NSString alloc]initWithFormat:@"no:%@ name:%@ class:%@", _studentNo, _studentName, _studentClass]autorelease];
}

- (void)dealloc {
    [_studentName release];
    [_studentClass release];
    [_studentNo release];
    [super dealloc];
}
@end

 

EncodingViewController.h

詳細見上。

EncodingViewController.m

- (IBAction)saveToArchiver:(id)sender {
    NSString* fileName = [self filePath:@"student.archiver"];
    NSMutableData* data = [NSMutableData data];
    NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];
    Student* student = [[Student alloc]init];
    student.studentNo = studentNo.text;
    student.studentName = studentName.text;
    student.studentClass = studentClass.text;
    [archiver encodeObject:student forKey:@"myStudent"];
    [archiver finishEncoding];
    [data writeToFile:fileName atomically:YES];
    [archiver release];
    [student release];
}

NSMutableData * theData = [NSMutableData data];用於包含編碼的數據。

NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:theData];創建NSKeyedArchiver實例,用於將對象歸檔到此theData實例中。

[archiver encodeObject:student forKey:@"mystudent"]; 使用“鍵-值”對編碼來對希望包含在歸檔中的對象進行歸檔。

[theData writeToFile:filename atomically:YES]; 寫入數據到歸檔文件。

EncodingViewController.m

- (IBAction)loadFromArchiver:(id)sender {
    NSString* fileName = [self filePath:@"student.archiver"];
    NSData* data = [NSData dataWithContentsOfFile:fileName];
    if ([data length] > 0) {
        NSKeyedUnarchiver* unArchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];
        Student* student = [unArchiver decodeObjectForKey:@"myStudent"];
        studentNo.text = student.studentNo;
        studentName.text = student.studentName;
        studentClass.text = student.studentClass;
        [unArchiver finishDecoding];
        [unArchiver release];
    }
}

 

NSData * theData =[NSData dataWithContentsOfFile:filename];從歸檔文件中獲得NSData實例。

NSKeyedUnarchiver * archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData]; 

創建一個NSKeyedUnarchiver實例對數據進行解碼。Student *student = [archiver decodeObjectForKey:@"mystudent"];

使用與歸檔編碼使用相同的鍵對象進行解碼。

9.5 訪問SQLite

SQLite數據庫

SQLite是一個開源的嵌入式關系數據庫,它在2000年由D. Richard Hipp發布,它的減少應用程序管理數據的開銷,SQLite可移植性好,很容易使用,很小,高效而且可靠。

SQLite嵌入到使用它的應用程序中,它們共用相同的進程空間,而不是單獨的一個進程。從外部看,它並不像一個RDBMS,但在進程內部,它卻是完整的,自包含的數據庫引擎。 嵌入式數據庫的一大好處就是在你的程序內部不需要網絡配置,也不需要管理。因為客戶端和服務器在同一進程空間運行。SQLite 的數據庫權限只依賴於文件系統,沒有用戶帳戶的概念。SQLite 有數據庫級鎖定,沒有網絡服務器。它需要的內存,其它開銷很小,適合用於嵌入式設備。你需要做的僅僅是把它正確的編譯到你的程序。

SQLite數據類型

SQLite是無類型的,這意味着你可以保存任何類型的數據到你所想要保存的任何表的任何列中, 無

論這列聲明的數據類型是什么,對於SQLite來說對字段不指定類型是完全有效的,如: 

Create Table ex1(a, b, c); 

SQLite允許忽略數據類型,但是仍然建議在你的Create Table語句中指定數據類型, 因為數據類型對於你和其他的程序員交流, 或者你准備換掉你的數據庫引擎。 SQLite支持常見的數據類型, 如:

wps_clip_image-15813

在iOS中使用SQLite3

為了能夠在iOS中使用SQLite3需要是將libsqlite3.dylib類庫添加到Xcode工程中,在工程的Frameworks(框架) 文件夾右鍵添加存在Frameworks

wps_clip_image-1001

或者導航到 /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/ iPhoneSimulator<version>.sdk/usr/lib 目錄下面找到libsqlite3.dylib.

實例:StudentSQLite3

wps_clip_image-24545

StudentSQLite3ViewController.h

#import "sqlite3.h"

#define DATA_FILE @"data.sqlite3"
#define TABLE_NAME @"student"
#define FIELDS_NAME_SID @"studentId"
#define FIELDS_NAME_SNAME @"studentName"
#define FIELDS_NAME_SCLASS @"studentClass"

@interface ViewController : UIViewController {
    sqlite3* db;
}
@property (retain, nonatomic) IBOutlet UITextField *studentId;
@property (retain, nonatomic) IBOutlet UITextField *studentName;
@property (retain, nonatomic) IBOutlet UITextField *studentClass;
- (IBAction)saveFromSqlite:(id)sender;
- (IBAction)loadFromSqlite:(id)sender;


-(NSString*)dataFile;
-(IBAction)textFieldDoneEditing:(id)sender;

@end

 

StudentSQLite3ViewController.m

@synthesize studentId;
@synthesize studentName;
@synthesize studentClass;

-(NSString*)dataFile {
    NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* myDocPath = [myPaths objectAtIndex:0];
    NSString* fileName = [myDocPath stringByAppendingFormat:DATA_FILE];
    return fileName;
}

 

無參數SQLite3處理過程

1、打開數據庫sqlite3_open。

2、創建數據庫表和執行SQL語句sqlite3_exec。

3、釋放資源sqlite3_close。

創建數據庫

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString* fileName = [self dataFile];
    NSLog(@"%@", fileName);
    if (sqlite3_open([fileName UTF8String], &db) != SQLITE_OK) {
        sqlite3_close(db);
        NSAssert(NO, @"OPEN SQLITE DATABASE ERROR!");
    } else {
        char* error;
        NSString* createSQL = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@ TEXT PRIMARY KEY, %@ TEXT, %@% TEXT);", 
                                  TABLE_NAME, FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS];
if (sqlite3_exec(db, [createSQL UTF8String], NULL, NULL, &error)) { sqlite3_close(db); NSAssert1(NO, @"CREATE TABLE ERROR", error); } else { sqlite3_close(db); } } }

 

sqlite3_open([[self dataFilePath] UTF8String], &db) != SQLITE_OK sqlite3_open打開數據庫,注意:在sqlite3中的函數都是使用C字符串[self dataFilePath] UTF8String]是將NSString字符串轉換為C字符串,&db是sqlite3指針(* db)的地址。

該函數sqlite3_open返回SQLITE_OK打開成功。

sqlite3_exec(db, [tablesql UTF8String], NULL, NULL, &err) != SQLITE_OK

sqlite3_exec是執行任何不帶返回值sql語句,第2個參數是要執行的sql語句,第3個參數是要回調函數,第4個參數是要回調函數的參數,第5個參數是執行出錯的字符串。

sqlite3_close(db); 是關閉數據庫。

NSAssert是斷言函數,當斷言失敗時候打印信息。

NSAssert1是帶有一個參數的NSAssert函數,此外還有NSAssert2等函數。

有參數的SQLite3處理過程

1、打開數據庫sqlite3_open。

2、預處理SQL語句sqlite3_prepare_v2。

3、綁定參數sqlite3_bind_text。

4、執行語句sqlite3_step(statement) 。

5、釋放資源sqlite3_finalize࿨sqlite3_close。

數據保存

- (IBAction)saveFromSqlite:(id)sender {
    NSString* fileName = [self dataFile];
    NSLog(@"%@", fileName);
    if (sqlite3_open([fileName UTF8String], &db)) {
        sqlite3_close(db);
        NSAssert(NO, @"OPEN DATABASE ERROR");
    } else {
        NSString* sqlStr = [NSString stringWithFormat:@"INSERT OR REPLACE INTO %@(%@, %@, %@) VALUES(?, ?, ?)",TABLE_NAME, FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS];
        sqlite3_stmt* statement;
        //預處理過程
        if (sqlite3_prepare(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) {
            //綁定參數開始
            sqlite3_bind_text(statement, 1, [studentId.text UTF8String], -1, NULL);
            sqlite3_bind_text(statement, 2, [studentName.text UTF8String], -1, NULL);
            sqlite3_bind_text(statement, 3, [studentClass.text UTF8String], -1, NULL);
            //執行插入
            if (sqlite3_step(statement) != SQLITE_DONE) {
                NSAssert(0, @"INSERT DATABASE ERROR!");
            }
        }
        sqlite3_finalize(statement);
        sqlite3_close(db);
    }
}

 

sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, nil) == SQLITE_OK

sqlite3_prepare_v2執行sql語句,第3個參數-1代表全部sql字符串長度,第4個參數&statement是sqlite3_stmt指針(* statement)的地址,第5個參數是sql語句沒有被執行的部分語句。

sqlite3_bind_text(statement, 1, [studentId.text UTF8String], -1, NULL);

是綁定參數,第2個參數為序號(從1開始),第3個參數為字符串值,第4個參數為字符串長度。 第5個參數為一個函數指針,SQLITE3執行完操作后回調此函數,通常用於釋放字符串占用的內存。

sqlite3_step(statement) != SQLITE_DONE判斷是否執行完成sql語句執行。

sqlite3_finalize(statement)和sqlite3_close(db)釋放資源。

查詢數據

- (IBAction)loadFromSqlite:(id)sender {
    NSString* fileName = [self dataFile];
    NSLog(@"%@", fileName);
    if (sqlite3_open([fileName UTF8String], &db) != SQLITE_OK) {
        sqlite3_close(db);
        NSAssert(NO, @"OPEN DATABASE ERROR!");
    } else {
        NSString* sqlStr = [NSString stringWithFormat:@"SELECT %@,%@,%@ FROM %@ WHERE %@=?", 
                            FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS, TABLE_NAME, FIELDS_NAME_SID];
        sqlite3_stmt* statement;
        //預處理過程
        if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) {
            //綁定參數開始
            sqlite3_bind_text(statement, 1, "1000", -1, NULL);
            //執行
            while (sqlite3_step(statement) == SQLITE_ROW) {
                char* field1 = (char*)sqlite3_column_text(statement, 0);
                NSString* field1Str = [[NSString alloc]initWithUTF8String:field1];
                studentId.text = field1Str;
                
                char* field2 = (char*)sqlite3_column_text(statement, 1);
                NSString* field2Str = [[NSString alloc]initWithUTF8String:field2];
                studentName.text = field2Str;
                
                char* field3 = (char*)sqlite3_column_text(statement, 2);
                NSString* field3Str = [[NSString alloc]initWithUTF8String:field3];
                studentClass.text = field3Str;
                
                [field1Str release];
                [field2Str release];
                [field3Str release];
            }
        }
        sqlite3_finalize(statement);
        sqlite3_close(db);
    }
}

 

while (sqlite3_step(statement) == SQLITE_ROW) sqlite3_step(statement) == SQLITE_ROW單步執行並判斷sql語句執行的狀態。

char *field1 = (char *) sqlite3_column_text(statement, 0); sqlite3_column_text(statement, 0);取出字段值,第2個參數是列的順序,序號是從0開始。

NSString *field1Str = [[NSString alloc] initWithUTF8String: field1];構建NSSting字符串。

其它部分代碼

-(IBAction)textFieldDoneEditing:(id)sender {
    [sender resignFirstResponder];
}
- (void)viewDidUnload
{
    [self setStudentId:nil];
    [self setStudentName:nil];
    [self setStudentClass:nil];
    [super viewDidUnload];
}

- (void)dealloc {
    [studentId release];
    [studentName release];
    [studentClass release];
    [super dealloc];
}

 

注:
1 本教程是基於關東升老師的教程
2 基於黑蘋果10.6.8和xcode4.2
3 本人初學,有什么不對的望指教
4 教程會隨着本人學習,持續更新
5 教程是本人從word筆記中拷貝出來了,所以格式請見諒


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM