在項(xiàng)目中我們常需要根據(jù)需求存儲(chǔ)一些信息,而數(shù)據(jù)持久化就顯得非常必要了,在這里我對(duì)數(shù)據(jù)持久化做個(gè)總結(jié)。
在說(shuō)數(shù)據(jù)持久化之前,先提一個(gè)概念,沙盒。相信大家對(duì)他并不陌生,iOS程序默認(rèn)情況下只能訪問(wèn)程序自己的目錄,這個(gè)目錄被稱(chēng)為沙盒。
沙盒路徑下有好幾個(gè)文件夾,每個(gè)文件夾都有自己的特性。詳細(xì)代碼如下:
//存放應(yīng)用程序的源文件,app
NSString *pathOne = [[NSBundle mainBundle] bundlePath];
NSLog(@"%@", pathOne);
//最常用的目錄Documents,iTunes同步該應(yīng)用時(shí)會(huì)同步此文件夾中的內(nèi)容,適合存儲(chǔ)重要數(shù)據(jù)。
NSString *pathTwo = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSLog(@"%@", pathTwo);
//caches:iTunes不會(huì)同步此文件夾,適合存儲(chǔ)體積大,不需要備份的非重要數(shù)據(jù)。Preferences:NSUserDefaults中存儲(chǔ),iTunes同步該應(yīng)用時(shí)會(huì)同步此文件夾中的內(nèi)容,通常保存應(yīng)用的設(shè)置信息。
NSString *pathThree = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSLog(@"%@", pathThree);
//iTunes不會(huì)同步此文件夾,系統(tǒng)可能在應(yīng)用運(yùn)行時(shí)就刪除該目錄下的文件,所以此目錄適合應(yīng)用中的一些臨時(shí)文件,用完就刪除
NSString *pathFour = NSTemporaryDirectory();
NSLog(@"%@",pathFour);
1.plist文件
plist文件是將某些特定的類(lèi),通過(guò)xml的方式保存在目錄中。
可以被序列化的類(lèi)型只有如下幾種:NSArray NSMutableArray NSDictionary NSMutableDictionary NSData NSMutableData NSString NSMutableString NSNumber NSDate
詳細(xì)代碼如下:
//1.獲取文件路徑
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *fileName = [path stringByAppendingPathComponent:@"fei.plist"];
//2.存儲(chǔ)
NSArray *array = @[@"1",@"2",@"3",@"4"];
[array writeToFile:fileName atomically:YES]; //atomically表示是否需要先寫(xiě)入一個(gè)輔助文件,再把輔助文件拷貝到目標(biāo)文件地址。這是更安全的寫(xiě)入文件方法,一般都寫(xiě)YES。
//3.讀取
NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
NSLog(@"%@", result);
2.NSUserDefaults
先上代碼,大家先看一下:
//1.獲得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中寫(xiě)入內(nèi)容
[userDefaults setObject:@"獅子" forKey:@"貓科動(dòng)物"];
[userDefaults setBool:YES forKey:@"公的母的"];
[userDefaults setInteger:5 forKey:@"多大"];
//3.立即同步
[userDefaults synchronize];
//4.讀取文件
NSString *name = [userDefaults objectForKey:@"貓科動(dòng)物"];
BOOL sex = [userDefaults boolForKey:@"公的母的"];
NSInteger age = [userDefaults integerForKey:@"多大"];
NSLog(@"%@, %d, %ld", name, sex, age);
一般在這里保存應(yīng)用程序的配置信息的(我們代駕項(xiàng)目中是把個(gè)人信息存儲(chǔ)在這里),一般不要在這里保存其他數(shù)據(jù)。
如果這里你沒(méi)有調(diào)用synchronize方法的話,系統(tǒng)會(huì)根據(jù)I/O情況不定時(shí)刻地保存到文件中。所以如果需要立即寫(xiě)入文件的就必須調(diào)用synchronize方法。
3.NSKeyedArchiver
歸檔在iOS中是另一種形式的序列化,只要遵循了NSCoding協(xié)議的對(duì)象都可以通過(guò)它實(shí)現(xiàn)序列化。由于決大多數(shù)支持存儲(chǔ)數(shù)據(jù)的Foundation和Cocoa Touch類(lèi)都遵循了NSCoding協(xié)議,因此,對(duì)于大多數(shù)類(lèi)來(lái)說(shuō),歸檔相對(duì)而言還是比較容易實(shí)現(xiàn)的。
1.需要在定義的類(lèi)中實(shí)現(xiàn)NSCoding協(xié)議 ,一個(gè)用來(lái)說(shuō)明如何將對(duì)象編碼到歸檔中,另一個(gè)說(shuō)明如何進(jìn)行解檔來(lái)獲取一個(gè)新對(duì)象。
//實(shí)現(xiàn)協(xié)議的方法
//解檔
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if ([super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
//歸檔
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
注意:如果需要?dú)w檔的類(lèi)是某個(gè)自定義類(lèi)的子類(lèi)時(shí),就需要在歸檔和解檔之前先實(shí)現(xiàn)父類(lèi)的歸檔和解檔方法。
2.接下來(lái)是使用,將創(chuàng)建類(lèi)的對(duì)象的數(shù)據(jù)進(jìn)行歸檔。
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"maozhuxi.data"];
Person *person = [[Person alloc] init];
person.name = @"毛主席";
person.age = 50;
//將對(duì)象歸檔是調(diào)用NSKeyedArchiver的工廠方法archiveRootObject
[NSKeyedArchiver archiveRootObject:person toFile:file];
然后在我們需要的地方解檔取出這些數(shù)據(jù)
//需要從文件中解檔對(duì)象就調(diào)用NSKeyedUnarchiver的一個(gè)工廠方法 unarchiveObjectWithFile:即可。
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"maozhuxi.data"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
if (person) {
NSLog(@"name == %@", person.name);
NSLog(@"age == %ld", person.age);
}
4.FMDB
大數(shù)據(jù)的存儲(chǔ)我們一般用FMDB,比如聊天中的信息一般是用FMDB存儲(chǔ)的(融云第三方就是這么存儲(chǔ)的。。。),他是在sqlite的基礎(chǔ)上進(jìn)行封裝的,因?yàn)閟qlite使用起來(lái)不是很好使,所以iOS的大神們封裝了這個(gè)FMDB,我也在這里記錄一下用法以及心得。
FMDB的下載地址:https://github.com/ccgus/fmdb
1.將FMDB第三方導(dǎo)入到工程中,如下圖:

2.在工程文件中導(dǎo)入FMDB.h
3.創(chuàng)建數(shù)據(jù)庫(kù),并建表格。如下面代碼
- (void)openDataBase {
//1.設(shè)置文件名
NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"xiaoping.db"];
//2.打開(kāi)數(shù)據(jù)庫(kù)文件,如果沒(méi)有會(huì)自動(dòng)創(chuàng)建一個(gè)文件
_database = [FMDatabase databaseWithPath:filename];
if (![_database open]) {
NSLog(@"數(shù)據(jù)庫(kù)打開(kāi)失敗");
}else {
[_database executeUpdate:@"create table myTable(name,age,sex)" ];
}
[_database close];
}
在指定路徑下創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)文件,并在其中創(chuàng)建一個(gè)表格,分別包含三個(gè)屬性。
4.向表格中添加數(shù)據(jù),如下代碼:
- (void)addSQL {
[_database open];
BOOL b = [_database executeUpdateWithFormat:@"insert into myTable(name,age,sex) values (%@,%d,%@);",@"junjie",23,@"man"];
BOOL c = [_database executeUpdateWithFormat:@"insert into myTable(name,age,sex) values (%@,%d,%@);",@"xiaohong",22,@"woman"];
[_database close];
b ? NSLog(@"junjie success"):NSLog(@"junjie fail");
c ? NSLog(@"xiaohong success"):NSLog(@"xiaohong fail");
}
在這里添加了兩條數(shù)據(jù),分別是junjie和xiaohong,注意一下語(yǔ)法。
5.查詢(xún)表格中的數(shù)據(jù),如下代碼:
- (void)requireSQL {
//取得資料
[_database open];
FMResultSet *rs = [_database executeQuery:@"select * from myTable"];
while ([rs next]) {//遍歷一下
NSString *name = [rs stringForColumn:@"name"];
int age = [rs intForColumn:@"age"];
NSString *sex = [rs stringForColumn:@"sex"];
NSLog(@"name == %@, age == %d, sex == %@",name, age, sex);
}
//這里是找出對(duì)應(yīng)的人的對(duì)應(yīng)信息
NSString *junjie_sex = [_database stringForQuery:@"select sex from myTable where name = ?",@"junjie"];
NSString *xiaohong_sex = [_database stringForQuery:@"select sex from myTable where name = ?",@"xiaohong"];
NSLog(@"junjie_sex == %@", junjie_sex);
NSLog(@"xiaohong_sex == %@", xiaohong_sex);
[rs close];
[_database close];
}
6.刪除操作,代碼如下:
- (void)delegateSQL {
[_database open];
[_database executeUpdate:@"delete from myTable where name = 'junjie'"];
[_database close];
}
上面,除了查詢(xún)是executeQuery語(yǔ)句,其余全是executeUpdate語(yǔ)句。對(duì)于sql語(yǔ)法不熟悉的童鞋,在這里參考一下我寫(xiě)的,如果還不明白,百度一下sql簡(jiǎn)單語(yǔ)法你就懂了(大學(xué)教的。。)
以上代碼敲出來(lái)后,有時(shí)候我們想通過(guò)圖形的方式查看,以前在大學(xué)學(xué)數(shù)據(jù)庫(kù)的時(shí)候 (那會(huì)還是windows),我們用的是MySQL,然后在dos下(終端)能夠?qū)⒈砀褚詧D文形式展現(xiàn)出來(lái),在這里我是用DB Browser工具操作的,如下圖:

很直觀的看見(jiàn)你建的表格數(shù)據(jù)。
5.CoreData
1.新建一個(gè)工程,并勾選coredata選項(xiàng):

2.建立好以后可以看到xxx.xcdatamodeld,在這里可以添加實(shí)體和實(shí)體的屬性。需要注意的是:實(shí)體名字必須以大寫(xiě)開(kāi)頭

3.然后新建一個(gè)file,記得是NSManagedObject cubclass

4.然后勾選自己的工程

5.勾選建立的實(shí)體

6.next后我們即可獲得四個(gè)文件

7.在viewcontrller中導(dǎo)入<CoreData/CoreData.h>
"Student.h"
"Student+CoreDataProperties.h"
然后定義成員變量
{
AppDelegate *app;
}
并在viewdidload中初始化
app = [UIApplication sharedApplication].delegate;
8.接下來(lái)是增刪改查
//增
- (void)coreDataAdd {
//初始化,name后面跟的是剛建的那個(gè)實(shí)體
Student *stu = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:[app managedObjectContext]];
stu.name = [NSString stringWithFormat:@"junjie%d",arc4random()%10];
stu.sex = @"man";
stu.age = [NSString stringWithFormat:@"%d", arc4random() % 15];
//保存
[app.managedObjectContext save:nil];
}
//刪除
- (void)coreDataDelegate {
//讀取這個(gè)類(lèi),entify:定義管理對(duì)象,managedObjectContext:被管理的對(duì)象上下文
NSEntityDescription *entify = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:app.managedObjectContext];
//建立請(qǐng)求,NSFetchRequest獲取數(shù)據(jù)的請(qǐng)求
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entify];
//設(shè)置檢索條件
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@",@"e3"];
[request setPredicate:predicate];
//遍歷所有學(xué)生,獲取所有學(xué)生的信息,存在數(shù)組array里面
NSArray *array = [app.managedObjectContext executeFetchRequest:request error:nil];
if (array.count) {
for (Student *stu in array) {
//刪除對(duì)象
[app.managedObjectContext deleteObject:stu ];
}
//保存結(jié)果
[app.managedObjectContext save:nil];
NSLog(@"delegate success");
} else {
NSLog(@"沒(méi)有檢索到數(shù)據(jù)");
}
}
//改
- (void)coreDataUpdate {
//讀取類(lèi)
NSEntityDescription *entify = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:app.managedObjectContext];
//建立請(qǐng)求
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entify];
//建立檢索條件
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name != %@", @"xiaohong"];
[request setPredicate:predicate];
//遍歷所有學(xué)生,獲取所有學(xué)生的信息,存在數(shù)據(jù)array里面
NSArray *array = [app.managedObjectContext executeFetchRequest:request error:nil];
if (array.count) {
for (Student *stu in array) {
stu.name = @"xiaohong";
}
//保存
[app.managedObjectContext save:nil];
NSLog(@"修改完成");
} else {
NSLog(@"沒(méi)有檢索結(jié)果");
}
}
//查
- (void)coreDataSelect {
//讀取這個(gè)類(lèi)
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:app.managedObjectContext];
//建立請(qǐng)求
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//建立請(qǐng)求的是哪一類(lèi)
[request setEntity:entity];
//遍歷所有學(xué)生,獲取所有學(xué)生的信息,存在數(shù)據(jù)array里面
NSArray *array = [app.managedObjectContext executeFetchRequest:request error:nil];
for (Student *stu in array) {
NSLog(@"%@", stu.name);
}
}
數(shù)據(jù)持久化常見(jiàn)的就這幾種,可以根據(jù)自己的需要選擇一到兩種使用。我一般用小數(shù)據(jù)存儲(chǔ)用NSUserDefaults,大數(shù)據(jù)存儲(chǔ)用FMDB,其他的相對(duì)來(lái)說(shuō)用的少。
最后,如果你喜歡我的文章,就給我大大的點(diǎn)個(gè)贊吧。