原文: 點(diǎn)擊查看
數(shù)據(jù)持久化 ,就是將數(shù)據(jù)保存起來,以便應(yīng)用程序或設(shè)備重啟后仍然可以訪問
- plist 文件 (屬性列表)
- preference (偏好設(shè)置)
- NSKeyedArchiver (歸檔)
- SQLite
- CoreData (它提供了對象-關(guān)系映射(ORM)的功能凝赛,即能夠?qū)C對象轉(zhuǎn)化成數(shù)據(jù)处嫌,保存在SQLite數(shù)據(jù)庫文件中孤页,也能夠?qū)⒈4嬖跀?shù)據(jù)庫中的數(shù)據(jù)還原成OC對象)
沙盒
只允許本程序訪問的目錄
目錄結(jié)構(gòu):
"應(yīng)用程序包"
Documents
Library
Caches
Preferences
tmp
應(yīng)用程序包
這里面存放的是應(yīng)用程序的源文件拘泞,包括資源文件和可執(zhí)行文件
NSLog(@"%@", [[NSBundle mainBundle] bundlePath]);
Documents
最常用的目錄逃糟,iTunes同步該應(yīng)用時會同步此文件夾中的內(nèi)容,適合存儲重要數(shù)據(jù)劈伴。
NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject);
Library/Caches
iTunes不會同步此文件夾弊攘,適合存儲體積大,不需要備份的非重要數(shù)據(jù)俭嘁。
NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject);
Library/Preferences
iTunes 會同步此文件夾, 保存用戶偏好設(shè)置
NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject);
tmp
iTunes不會同步此文件夾躺枕,系統(tǒng)可能在應(yīng)用沒運(yùn)行時就刪除該目錄下的文件,所以此目錄適合保存應(yīng)用中的一些臨時文件供填,用完就刪除拐云。
NSLog(@"%@", NSTemporaryDirectory());
plist 文件
將某些特定的類通過 xml 方式存在目錄中
可被序列化的類型:
- NSArray;
- NSMutableArray;
- NSDictionary;
- NSMutableDictionary;
- NSData;
- NSMutableData;
- NSString;
- NSMutableString;
- NSNumber;
- NSDate;
獲得文件路徑
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"];
存儲
NSArray *array = @[@"123", @"456", @"789"];
[array writeToFile:fileName atomically:YES];
讀取
NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
NSLog(@"%@", result);
- 只有以上列出的類型才能使用 plist 文件存儲。
- 存儲時使用
writeToFile: atomically:
方法近她。 其中atomically
表示是否需要先寫入一個輔助文件叉瘩,再把輔助文件拷貝到目標(biāo)文件地址。這是更安全的寫入文件方法粘捎,一般都寫YES
薇缅。- 讀取時使用
arrayWithContentsOfFile:
方法危彩。
Preference
//1.獲得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中寫入內(nèi)容
[userDefaults setObject:@"AAA" forKey:@"a"];
[userDefaults setBool:YES forKey:@"sex"];
[userDefaults setInteger:21 forKey:@"age"];
//2.1立即同步
[userDefaults synchronize];
//3.讀取文件
NSString *name = [userDefaults objectForKey:@"a"];
BOOL sex = [userDefaults boolForKey:@"sex"];
NSInteger age = [userDefaults integerForKey:@"age"];
NSLog(@"%@, %d, %ld", name, sex, age);
- 偏好設(shè)置是專門用來保存應(yīng)用程序的配置信息的,一般不要在偏好設(shè)置中保存其他數(shù)據(jù)泳桦。
- 如果沒有調(diào)用synchronize方法汤徽,系統(tǒng)會根據(jù)I/O情況不定時刻地保存到文件中。所以如果需要立即寫入文件的就必須調(diào)用synchronize方法灸撰。
- 偏好設(shè)置會將所有數(shù)據(jù)保存到同一個文件中谒府。即preference目錄下的一個以此應(yīng)用包名來命名的plist文件。
歸檔 NSKeyedArchiver
因為前兩者都有一個致命的缺陷浮毯,只能存儲常用的類型完疫。但只要遵循了NSCoding協(xié)議的對象都可以通過它實(shí)現(xiàn)序列化。由于決大多數(shù)支持存儲數(shù)據(jù)的Foundation和Cocoa Touch類都遵循了NSCoding協(xié)議, 所以歸檔可以實(shí)現(xiàn)把自定義的對象存放在文件中债蓝。
NSCoding 協(xié)議
NSCoding協(xié)議聲明了兩個方法壳鹤,這兩個方法都是必須實(shí)現(xiàn)的。一個用來說明如何將對象編碼到歸檔中饰迹,另一個說明如何進(jìn)行解檔來獲取一個新對象芳誓。
- 遵循 NSCoding 協(xié)議
//1.遵循NSCoding協(xié)議
@interface Person : NSObject //2.設(shè)置屬性
@property (strong, nonatomic) UIImage *avatar;
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
@end
- 實(shí)現(xiàn)協(xié)議方法
// 反歸檔
- (id)initWithCoder:(NSCoder *)aDecoder {
if ([super init]) {
self.avatar = [aDecoder decodeObjectForKey:@"avatar"];
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
// 歸檔
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.avatar forKey:@"avatar"];
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
如果需要?dú)w檔的類是某個自定義類的子類時,就需要在歸檔和解檔之前先實(shí)現(xiàn)父類的歸檔和解檔方法蹦锋。即
super encodeWithCoder:aCoder
和super initWithCoder:aDecoder
方法;
- 歸檔
需要把對象歸檔時調(diào)用NSKeyedArchiver
的工廠方法archiveRootObject: toFile:
方法兆沙。
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [[Person alloc] init];
person.avatar = self.avatarView.image;
person.name = self.nameField.text;
person.age = [self.ageField.text integerValue];
[NSKeyedArchiver archiveRootObject:person toFile:file];
- 反歸檔
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
if (person) {
self.avatarView.image = person.avatar;
self.nameField.text = person.name;
self.ageField.text = [NSString stringWithFormat:@"%ld", person.age];
}
** 注意 **
- 必須遵循并實(shí)現(xiàn)NSCoding協(xié)議
- 保存文件的擴(kuò)展名可以任意指定
- 繼承時必須先調(diào)用父類的歸檔解檔方法
SQLite 3
之前的所有存儲方法,都是覆蓋存儲莉掂。如果想要增加一條數(shù)據(jù)就必須把整個文件讀出來葛圃,然后修改數(shù)據(jù)后再把整個內(nèi)容覆蓋寫入文件。所以它們都不適合存儲大量的內(nèi)容憎妙。
SQLite 中可以將數(shù)據(jù)分為以下類型:
- integer : 整數(shù)
- real : 實(shí)數(shù)(浮點(diǎn)數(shù))
- text : 文本字符串
- blob : 二進(jìn)制數(shù)據(jù)库正,比如文件,圖片之類的
** 注: ** 主鍵必須設(shè)置成integer.
1. 導(dǎo)入依賴庫
導(dǎo)入 libsqlite3.dylib
并導(dǎo)入主頭文件厘唾。
2. 創(chuàng)建數(shù)據(jù)庫并打開
操作數(shù)據(jù)庫之前必須先指定數(shù)據(jù)庫文件和要操作的表褥符,所以使用SQLite3,首先要打開數(shù)據(jù)庫文件抚垃,然后指定或創(chuàng)建一張表喷楣。
/**
* 打開數(shù)據(jù)庫并創(chuàng)建一個表
*/
- (void)openDatabase {
//1.設(shè)置文件名
NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];
//2.打開數(shù)據(jù)庫文件,如果沒有會自動創(chuàng)建一個文件
NSInteger result = sqlite3_open(filename.UTF8String, &_sqlite3);
if (result == SQLITE_OK) {
NSLog(@"打開數(shù)據(jù)庫成功鹤树!");
//3.創(chuàng)建一個數(shù)據(jù)庫表
char *errmsg = NULL;
sqlite3_exec(_sqlite3, "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)", NULL, NULL, &errmsg);
if (errmsg) {
NSLog(@"錯誤:%s", errmsg);
} else {
NSLog(@"創(chuàng)表成功铣焊!");
}
} else {
NSLog(@"打開數(shù)據(jù)庫失敗罕伯!");
}
}
3. 執(zhí)行命令
使用 sqlite3_exec()
方法可以執(zhí)行任何SQL語句曲伊,比如創(chuàng)表、更新追他、插入和刪除操作坟募。但是一般不用它執(zhí)行查詢語句岛蚤,因為它不會返回查詢到的數(shù)據(jù)。
// 往表中插入數(shù)據(jù)
NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_person (name, age) VALUES('%@', '%ld')", nameStr, age];
char *errmsg = NULL;
sqlite3_exec(_sqlite3, sql.UTF8String, NULL, NULL, &errmsg);
4. 查詢命令
- sqlite3_prepare_v2() : 檢查sql的合法性
- sqlite3_step() : 逐行獲取查詢結(jié)果懈糯,不斷重復(fù)涤妒,直到最后一條記錄
- sqlite3_coloum_xxx() : 獲取對應(yīng)類型的內(nèi)容,iCol對應(yīng)的就是 SQL語句中字段的順序昂利,從0開始届腐。根據(jù)實(shí)際查詢字段的屬性,使用sqlite3_column_xxx取得對應(yīng)的內(nèi)容即可蜂奸。
- sqlite3_finalize() : 釋放stmt
/**
* 從表中讀取數(shù)據(jù)到數(shù)組中
*/
- (void)readData {
NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1000];
char *sql = "select name, age from t_person;";
sqlite3_stmt *stmt;
NSInteger result = sqlite3_prepare_v2(_sqlite3, sql, -1, &stmt, NULL);
if (result == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
char *name = (char *)sqlite3_column_text(stmt, 0);
NSInteger age = sqlite3_column_int(stmt, 1);
//創(chuàng)建對象
Person *person = [Person personWithName:[NSString stringWithUTF8String:name] Age:age];
[mArray addObject:person];
}
self.dataList = mArray;
}
sqlite3_finalize(stmt);
}
CoreData
CoreData 參考這位大神的簡書, 寫得非常詳細(xì).
鏈接: 我要娶你做我的CoreData! 原文