在開發通常需要我們把一些數據存儲在本地,這篇我們來介紹iOS中數據持久化得方法。
1.屬性列表
2.對象歸檔
3.偏好設置
4.嵌入式數據庫(SQLite3)
5.蘋果公司提供的持久化工具 Core Data
以下我們圍繞這4種方式逐一介紹,在上一遍種我們介紹了“沙盒”和文件管理的基礎知識,我們將以上一篇為基礎進行介紹后面內容
屬性列表(plist),指定應用的配置比如tabbar的狀態,黑白名單,網絡請求ATS等等。我們可以通過Xcode或者PRoperty List Editor手動編輯。并且只要字典或數組包含特定可序列化對象就可以將NSDictionary和NSArray實例寫入屬性列表或者創建。
序列化對象(serialized object)是指可以被轉換為字節流以便于存儲到文件中或者通過網絡進行傳輸的對象。
可以被序列化得Objective-C類:
NSArray;NSMutableArray;NSDictionary;NSMutableDictionary;NSData;NSMutableData;NSString;NSMutableString;NSNumber;NSDate;我們按照上一篇中的方法獲取需要存取的目錄然后執行保存和讀取的操作
//獲取路徑 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSString *fileName = [path stringByAppendingPathComponent:@"test.plist"];//保存數組NSArray *array = @[@"Gavin", @"Alice", @"Lucy"];[array writeToFile:fileName atomically:YES];//讀取信息NSArray *result = [NSArray arrayWithContentsOfFile:fileName];atomically:參數讓該方法將數據寫入輔助文件,而不是寫入指定位置。成功寫入該文件之后,輔助文件將被復制到第一個參數指定的位置,這是更安全的寫入文件的方法,如果應用在保存期間崩潰,則現有文件不會被破壞。
屬性列表中只能將一小部分對象存儲,我們來看看比較強大的方法。
我們先來介紹一些基礎的概念:
歸檔(archiving):是另一種形式的序列化,它可以在任何對象進行序列化,編寫用于保存數據,可以輕松的將復雜的對象寫入文件,然后再從中讀取他們。
實現對象歸檔,都應該遵循NSCoding協議同時還應該遵循NSCopying協議,NSCopying可以允許復制對象,由于決大多數支持存儲數據的Foundation和Cocoa Touch類都遵循了NSCoding協議,因此,對于大多數類來說,歸檔相對而言還是比較容易實現的。
NSCoding協議聲明了兩個方法,也就是歸檔和解碼。歸檔將一個對象編碼到歸檔中,解碼對歸檔解碼來創建一個新對象。我們通過下面的例子來了解
@interface Person : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, assgin) NSInteger age; @end //歸檔 - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInteger:self.age forKey:@"age"]; }//解檔 - (id)initWithCoder:(NSCoder *)aDecoder { if ([super init]) { self.name = [aDecoder decodeObjectForKey:@"name"]; self.age = [aDecoder decodeIntegerForKey:@"age"]; } return self; }實現這兩個方法,就可以對對象的屬性進行編碼和解碼,然后便可以對對象進行歸檔,并且將其寫入歸檔或從歸檔中讀取他們。
遵循NSCopying協議可以實現對象的復制,NSCopying有一個方法copyWithZone:方法實現它既可
-(id)copyWithZone:(NSZone *)zone{ Persion *obj = [[[self class] allocWithZone:zone]init]; obj.name = [self.name copyWithZone:zone]; obj.age = self.age; return obj}NSZone:參數它是指向系統用于管理內存的struct,現在的iOS完全可以忽略。
首先創建一個NSMutableData實例,用于包含編碼的數據,然后創建NSKeyedArchiver實例,用于將對象歸檔到NSMutableData實例中
//用于存放編碼數據NSMutableData *data = [[NSMutableData alloc] init];//編碼類NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];//創建Persion類Persion *teach = [[Persion alloc] init];teach.name = @"Gavin";teach.age = 25;//歸檔[archiver encodeObject:teach forKey:@"TeachPersion"];//完成編碼[archiver finnishEncoding];//獲取路徑 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSString *fileName = [path stringByAppendingPathComponent:@"test"];//寫入文件 成功返回YES 失敗返回NOBOOL success = [data writeToFile:fileName atomically:YES];//解碼NSData *decoderData = [[NSData alloc] initWithContentsOfFile:fileName];NSKeyedArchiver *unarchiver = [[NSKeyedArchiver alloc] initForReadingWithData:decoderData ];Persion *gavin = [Persion alloc] init];gavin = [unarchiver decodeObjectForKey:@"TeachPersion"];[unarchiver finishDecoding];/*也可使用歸檔[NSKeyedArchiver archiveRootObject:person toFile:fileName];解碼[NSKeyedUnarchiver unarchiveObjectWithFile:file];*/注意:如果需要歸檔的類是某個自定義類的子類時,就需要在歸檔和解檔之前先實現父類的歸檔和解檔方法。即 [super encodeWithCoder:aCoder] 和 [super initWithCoder:aDecoder] 方法;
偏好設置是專門用來保存應用程序的配置信息的,一般不要在偏好設置中保存其他數據。如果沒有調用synchronize方法,系統會根據I/O情況不定時刻地保存到文件中。所以如果需要立即寫入文件的就必須調用synchronize方法。偏好設置會將所有數據保存到同一個文件中。即preference目錄下的一個以此應用包名來命名的plist文件。
//1.獲得NSUserDefaults文件NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];//2.向文件中寫入內容[userDefaults setObject:@"teach" forKey:@"name"];[userDefaults setInteger:21 forKey:@"age"];//2.1立即同步[userDefaults synchronize];//3.讀取文件NSString *name = [userDefaults objectForKey:@"name"];NSInteger age = [userDefaults integerForKey:@"age"];SQLite3是iOS中嵌入式SQL數據庫,SQLite3在儲存和檢索數據方面非常有效,它能夠對數據進行復雜的聚合,速度更快。
SQLite3使用SQL(Structured Query Language結構化查詢語言),數據庫基本介紹可以點擊這里,下面我們來看數據庫的使用。
我們要使用使用SQLite3需要連接libsqlite3.dylib的動態庫
使用SQLite3是比較麻煩的可以使用現有的框架FMDB,可以再GitHub上搜索。 以下總結以下FMDB的一些方法方便查閱引用對FMDB的使用,也可以訪問GitHub-FMDB。
FMDB有三個主要的類:
FMDatabase 一個FMDatabase對象就代表一個單獨的SQLite數據庫,用來執行SQL語句
FMResultSet 使用FMDatabase執行查詢后的結果集
FMDatabaseQueue 用于在多線程中執行多個查詢或更新,它是線程安全的
和c語言框架一樣,FMDB通過指定SQLite數據庫文件路徑來創建FMDatabase對象,但FMDB更加容易理解,使用起來更容易,使用之前一樣需要導入sqlite3.dylib。打開數據庫方法如下:
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];FMDatabase *database = [FMDatabase databaseWithPath:path]; if (![database open]) { NSLog(@"數據庫打開失敗!");}值得注意的是,Path的值可以傳入以下三種情況:
具體文件路徑,如果不存在會自動創建 空字符串@”“,會在臨時目錄創建一個空的數據庫,當FMDatabase連接關閉時,數據庫文件也被刪除 nil,會創建一個內存中臨時數據庫,當FMDatabase連接關閉時,數據庫會被銷毀
在FMDB中,除查詢以外的所有操作,都稱為“更新”, 如:create、drop、insert、update、delete等操作,使用executeUpdate:方法執行更新:
//常用方法有以下3種: - (BOOL)executeUpdate:(NSString*)sql, ...- (BOOL)executeUpdateWithFormat:(NSString*)format, ...- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments//示例[database executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)"]; //或者 [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES(?, ?)", @"Bourne", [NSNumber numberWithInt:42]];在多個線程中同時使用一個FMDatabase實例是不明智的。不要讓多個線程分享同一個FMDatabase實例,它無法在多個線程中同時使用。 如果在多個線程中同時使用一個FMDatabase實例,會造成數據混亂等問題。所以,請使用 FMDatabaseQueue,它是線程安全的。以下是使用方法:
創建隊列。FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];使用隊列[queue inDatabase:^(FMDatabase *database) { [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_1", [NSNumber numberWithInt:1]]; [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_2", [NSNumber numberWithInt:2]]; [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_3", [NSNumber numberWithInt:3]]; FMResultSet *result = [database executeQuery:@"select * from t_person"]; while([result next]) { } }];而且可以輕松地把簡單任務包裝到事務里:
[queue inTransaction:^(FMDatabase *database, BOOL *rollback) { [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_1", [NSNumber numberWithInt:1]]; [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_2", [NSNumber numberWithInt:2]]; [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_3", [NSNumber numberWithInt:3]]; FMResultSet *result = [database executeQuery:@"select * from t_person"]; while([result next]) { } //回滾 *rollback = YES; }];FMDatabaseQueue 后臺會建立系列化的G-C-D隊列,并執行你傳給G-C-D隊列的塊。這意味著 你從多線程同時調用調用方法,GDC也會按它接收的塊的順序來執行。
Core Data是一款穩定、功能全面的持久化工具。我們先看代碼實現上面的操作
- (void)test{ //獲取app代理 AppDelegate *appDelegate = [UIapplication shareApplication].delegate; //創建上下文 NSManageObjectContext *context = [appDelegate manaedObjectContext]; //創建請求 NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"test"]; //存儲 NSManageObject *coreData= NSEntitydescription insertNewObjectForEntityForName:@"test" inManagedObjectContext:context]; [coreData setValue:teach.name forKey:@"name"]; [coreData setValue:teach.age forKey:@"age"]; //保存上下文 [appDelegate saveContext]; //上下文返回庫中每一個對象 NSError *error; NSArray *objects = [context executeFetchRequest:request error &error]; //便利數組查找 //我們只存了一條數據那么我們只去第一個 Persion *teach = [[Persion alloc] init]; teach.name = [objects[0] valueForkey:@"name"]; teach.age = [objects[0] valueForkey:@"age"];}需要注意的是:我們在數據操作的時候需要監控app的生命周期以保證數據的穩定。
Core Data 更加詳細的用法大家可以查看官方文檔,API介紹非常詳細,在創建程序的記得勾選使用Core Data。
聯系寫了5個小時,也就是現在有時間。其中一些怕介紹不全,之后慢慢補充。發現有問題請評論
新聞熱點
疑難解答