關(guān)于數(shù)據(jù)持久化
數(shù)據(jù)持久化,字面上的意思來(lái)理解榄攀,就是將數(shù)據(jù)存放更長(zhǎng)時(shí)間嗜傅。
很顯然的,當(dāng)我們?cè)谄綍r(shí)開(kāi)發(fā)中檩赢,必然而然的會(huì)使用到數(shù)據(jù)的持久化吕嘀,但是通用的幾種方式,我們應(yīng)該了然于心贞瞒。以便于在使用到的時(shí)候币他,能夠更好的選擇使用哪一種。
通常數(shù)據(jù)持久化的幾種方式
1.NSUserDefaults
2.NSKeyedArchiver(對(duì)象模型歸檔)
3.SQLite
4.Core Data
1.NSUserDefaults
NSUserDefaults是蘋果提供的一種偏好設(shè)置的方法憔狞,將我們需要的一些偏好設(shè)置寫道plist文件當(dāng)中蝴悉。例如,記錄主題設(shè)置的模式瘾敢,是否自動(dòng)登錄等等拍冠。
NSUserDefaults是蘋果提供的一個(gè)單例,使用方法很簡(jiǎn)單簇抵,如下:
////write
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"daytime" forKey:@"themeStyle"];
[defaults synchronize];
////read
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *themeStyle= [defaults objectForKey:@"themeStyle"]
類似于此庆杜,NSUserDefaults 還持支NSData,NSString碟摆,NSNumber晃财,NSDate,NSArray典蜕,NSDictionary這些數(shù)據(jù)類型断盛。
當(dāng)然蘋果還提供了具體類型的讀寫方法:
- setBool:forKey:
- setFloat:forKey:
...等
但是,寫數(shù)據(jù)的時(shí)候特別注意的是這一句話
[defaults synchronize];
當(dāng)我們利用NSUserDefaults 來(lái)寫入數(shù)據(jù)的時(shí)候愉舔,系統(tǒng)默認(rèn)并不是馬上就會(huì)寫道plist里面钢猛,但是往往我們需要存一些偏好設(shè)置的時(shí)候,想法是寫進(jìn)去轩缤,plist里面就會(huì)馬上響應(yīng)命迈,就可以馬上讀出來(lái)贩绕。如果想實(shí)現(xiàn)該需求,就需要在我們存儲(chǔ)數(shù)據(jù)的最后面加上synchronize來(lái)同步數(shù)據(jù)到pilst里面壶愤。
2.NSKeyedArchiver與沙盒
NSKeyedArchiver 對(duì)象歸檔淑倾,就是當(dāng)我們需要將自定義的類對(duì)象進(jìn)行二進(jìn)制化歸檔。而沙盒存儲(chǔ)呢征椒,簡(jiǎn)單的來(lái)說(shuō)娇哆,就是將文件給存到本地磁盤中。
NSKeyedArchiver
首先陕靠,要使用NSKeyedArchiver歸檔,就需要實(shí)現(xiàn)NSCoding協(xié)議脱茉。同時(shí)實(shí)現(xiàn)其序列化和反序列化這兩個(gè)方法
//反序列化
//從coder中讀取數(shù)據(jù)剪芥,保存到相應(yīng)的變量中
- (id)initWithCoder:(NSCoder *)aDecoder;
// 序列化
//讀取實(shí)例變量琴许,并把這些數(shù)據(jù)寫到coder中税肪。
-(void)encodeWithCoder:(NSCoder *)coder;
沙盒
關(guān)于ios沙盒機(jī)制榜田,簡(jiǎn)單來(lái)說(shuō)就是一個(gè)安全權(quán)限益兄。默認(rèn)權(quán)限下,蘋果只允許IOS應(yīng)用程序訪問(wèn)程序自己的目錄箭券,該目錄就稱為“沙盒”净捅。
下圖是沙河目錄
MyApp.app
存放應(yīng)用程序本身的數(shù)據(jù),整個(gè)目錄是只讀的
不會(huì)被iTunes同步
Documents
存放應(yīng)用程序的數(shù)據(jù)文件(不可再生的)
會(huì)被iTunes同步
Documents/Inbox
存放保存由外部應(yīng)用請(qǐng)求當(dāng)前應(yīng)用程序打開(kāi)的文件
會(huì)被同步
Library
蘋果建議用來(lái)存放默認(rèn)設(shè)置或其它狀態(tài)信息
會(huì)被iTunes同步辩块,但是除了Caches子目錄
Library/Caches
主要是緩存文件
不會(huì)被iTunes同步
Library/Preferences
應(yīng)用程序的偏好設(shè)置文件蛔六。例如NSUserDefaults寫的設(shè)置數(shù)據(jù)都會(huì)保存到該目錄下的一個(gè)plist文件中
會(huì)被iTunes同步
tmp
各種臨時(shí)文件,保存應(yīng)用再次啟動(dòng)時(shí)不需要的文件废亭,并且隨時(shí)有可能被系統(tǒng)清理掉
不會(huì)被iTunes同步
簡(jiǎn)單的使用
了解了NSKeyedArchiver需要什么国章,沙盒是什么了,下面我們就開(kāi)始來(lái)看代碼:
@interface Person: NSObject <NSCoding>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *sex;
@end
//...
//實(shí)現(xiàn)序列化和反序列化
@implementation Person
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.sex forKey:@"sex"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self.name = [aDecoder decodeObjectForKey:@"name"];
self.sex= [aDecoder decodeObjectForKey:@"sex"];
return self;
}
@end
Person* person= [[Person alloc] init];
person.name = @"zhu";
person.age = @"man";
];
//歸檔,調(diào)用encodeWithCoder方法
NSData * perData= [NSKeyedArchiver archivedDataWithRootObject:person];
//讀取歸檔數(shù)據(jù),調(diào)用initWithCoder
Person* per= [NSKeyedUnarchiver unarchiveObjectWithData:perData];
//也可以將序列化的對(duì)象保存到沙盒中
//獲得Document的路徑
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//拓展名可以自己隨便取
NSString *path = [documents stringByAppendingPathComponent:@"person.archiver"];
[NSKeyedArchiver archiveRootObject:person toFile:path];
//讀取沙盒中的文件
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [documents stringByAppendingPathComponent:@"person.archiver"];
person *person1 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
3.SQLite
SQLite是一個(gè)輕量級(jí)關(guān)系數(shù)據(jù)庫(kù),最初的設(shè)計(jì)目標(biāo)是用于嵌入式系統(tǒng),它占用資源非常少.在iOS中,只需要加入li’blibsqlite3.0依賴以及引入sqlite3.h頭文件即可豆村。
關(guān)于sqlite在ios中的使用液兽,我并沒(méi)有深入的了解過(guò),所以這里只是簡(jiǎn)單的介紹下掌动。
4.Core Data
Core Data是iOS5之后才出現(xiàn)的一個(gè)框架四啰,它提供了對(duì)象-關(guān)系映射(ORM)的功能,即能夠?qū)C對(duì)象轉(zhuǎn)化成數(shù)據(jù)粗恢,保存在SQLite數(shù)據(jù)庫(kù)文件中拟逮,也能夠?qū)⒈4嬖跀?shù)據(jù)庫(kù)中的數(shù)據(jù)還原成OC對(duì)象。
至于到底是用coredata還是sqlite适滓,大家是各持己見(jiàn)敦迄,但是我覺(jué)得還是應(yīng)該看具體的業(yè)務(wù)邏輯。不要局限于某一個(gè)工具,而是在合適的時(shí)候選擇合適的方法就好罚屋。
關(guān)于coredata我們要知道的幾個(gè)東西
1.NSManagedObjectContext(托管對(duì)象上下文)
負(fù)責(zé)應(yīng)用和數(shù)據(jù)庫(kù)之間的交互苦囱,例如增刪查改。
2.NSManagedObjectModel(托管對(duì)象模型)
負(fù)責(zé)coredara里面的模型文件
3.NSPersistentStoreCoordinator(持久化存儲(chǔ)協(xié)調(diào)器)
負(fù)責(zé)添加持久性數(shù)據(jù)庫(kù)脾猛,例如sqlite撕彤,Binary、XML猛拴、或In-Memory等羹铅。但要注意這點(diǎn),Binary和XML格式的存儲(chǔ)區(qū)是Atomic愉昆,意思是就算我們只想修改少量的數(shù)據(jù)职员,但是在保存的時(shí)候還是得將整個(gè)文件都寫進(jìn)磁盤。
對(duì)于coredata跛溉,初學(xué)者不要覺(jué)得很神奇焊切,簡(jiǎn)單的想成是一個(gè)對(duì)象關(guān)系的數(shù)據(jù)庫(kù)就行了,不需要寫sql語(yǔ)句芳室,就能實(shí)現(xiàn)數(shù)據(jù)的存取专肪。
下面是一些簡(jiǎn)單的使用:
- (IBAction)addClick:(id)sender {
NSLog(@"addClick");
Employee *em = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:self.ctx];
em.name = @"lisi";
em.height = @172;
em.age = @25;
NSError *error = nil;
[self.ctx save:&error];
if (error) {
NSLog(@"%@===error", error);
} else {
NSLog(@"=====add=success====");
}
}
- (IBAction)readClick:(id)sender {
NSLog(@"readClick");
NSEntityDescription *enty = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:self.ctx];
NSFetchRequest *featch = [[NSFetchRequest alloc] init];
[featch setEntity:enty];
// NSPredicate *pre = [NSPredicate predicateWithFormat:@"name=%@",@"lisi"];
//
// [featch setPredicate:pre];
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:YES];
featch.sortDescriptors = @[sort];
NSArray *arr = [self.ctx executeFetchRequest:featch error:nil];
for (Employee *em in arr) {
NSLog(@"姓名:%@ 身高%@",em.name ,em.height);
}
}
- (IBAction)deleteClick:(id)sender {
NSLog(@"deleteClick");
NSEntityDescription *enty = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:self.ctx];
NSFetchRequest *featch = [[NSFetchRequest alloc] init];
featch.entity = enty;
featch.predicate = [NSPredicate predicateWithFormat:@"name=%@",@"lisi"];
NSArray *arr = [self.ctx executeFetchRequest:featch error:nil ];
for (Employee *em in arr) {
NSLog(@"姓名:%@ 身高%@",em.name ,em.height);
[self.ctx deleteObject:em];
}
[self.ctx save:nil];
}
- (IBAction)updateClick:(id)sender {
NSLog(@"updateClick");
NSEntityDescription *enty = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:self.ctx];
NSFetchRequest *featch = [[NSFetchRequest alloc] init];
featch.entity = enty;
featch.predicate = [NSPredicate predicateWithFormat:@"name=%@",@"lisi"];
NSArray *arr = [self.ctx executeFetchRequest:featch error:nil ];
for (Employee *em in arr) {
NSLog(@"姓名:%@ 身高%@",em.name ,em.height);
em.name = @"wangwu";
}
[self.ctx save:nil];
}
- (NSManagedObjectContext *)ctx
{
if (!_ctx) {
_ctx = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
NSManagedObjectModel *mModel = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mModel];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *url = [path stringByAppendingPathComponent:@"zhu.sqlite"];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:url] options:nil error:nil];
_ctx.persistentStoreCoordinator = coordinator;
}
return _ctx;
}
結(jié)束語(yǔ)
其實(shí)關(guān)于ios中數(shù)據(jù)持久化的使用,不會(huì)僅限于此堪侯,以上只是一些簡(jiǎn)單的介紹和使用的方法嚎尤。在當(dāng)說(shuō)到數(shù)據(jù)持久化的時(shí)候,我們腦海中應(yīng)該蹦出一些自己的理解來(lái)伍宦。自己心中理一個(gè)適合自己的知識(shí)結(jié)構(gòu)來(lái)幫助我們打理邏輯诺苹,然后在實(shí)際使用場(chǎng)景的時(shí)候,根據(jù)具體業(yè)務(wù)來(lái)選擇具體使用哪一種數(shù)據(jù)持久化方案雹拄。我認(rèn)為做開(kāi)發(fā)的不要一腦門的死記硬背收奔,一腦門的按照別人的說(shuō)的來(lái)做∽揖粒看到別人的邏輯的時(shí)候坪哄,覺(jué)得好的,自己心里領(lǐng)會(huì)下势篡,覺(jué)得不好的翩肌,自己在想想怎么去做會(huì)更加完美。千萬(wàn)不要做代碼的奴隸禁悠,而是以一種藝術(shù)家念祭,建筑師又或者設(shè)計(jì)師的角度來(lái)審視代碼。