分類
簡單來說范抓,IOS的數(shù)據(jù)持久化有四種方式:
- 屬性列表
- 對(duì)象歸檔
- iOS的嵌入式關(guān)系數(shù)據(jù)庫SQLite3
- 蘋果公司提供的持久化工具Core Data
下面以帶有四個(gè)TextLabel的界面為例埠况,這個(gè)界面的功能是用戶在TextLabel中輸入值枉长,系統(tǒng)將輸入的內(nèi)容的保存起來,再次啟動(dòng)程序的時(shí)候挨决,TextLabel默認(rèn)顯示的仍然是用戶之前輸入的值抽减。分別用IOS數(shù)據(jù)持久化的四種方式來實(shí)現(xiàn)上述功能。
完成這個(gè)例子需要三步——
- 保存label中的值就是寫文件
- label顯示默認(rèn)值就是讀文件例嘱;
- 選擇相應(yīng)的持久化方式,依靠這種方式進(jìn)行讀寫宁舰。
屬性列表
//1.讀
-(void)viewDidLoad{
//通過NSFileManager來檢查數(shù)據(jù)是否存在
NSString *filePath = [self dataFilePath]
if( [[NSFileManager defaultManager] fileExistsAtPath:filePath] ){
//文件存在 do something
//這就把文件讀出來了,讀的方法是拼卵,聲明一個(gè)與文件內(nèi)容相同類型的變量,取出即可
NSArray *array = [[NSArray alloc]initWithContentOfFile: filePath];
//賦值到需要的地方
UILabel *lable = self.textLabel;
label.text = array[0];
/*
NOTE :
self.textLabel為該頁面的UI變量蛮艰,最后的目的是要把咋文件讀出來的值顯示在這里
label先把指向self.textLabel的指針取到腋腮,然后由label來取值
***為何不直接 self.textLabel.text = array[0]; 呢?壤蚜?
*/
}
//下面是與歸檔編解碼無關(guān)的部分即寡,監(jiān)聽self,如果發(fā)生應(yīng)用終止運(yùn)行則及時(shí)保存數(shù)據(jù)
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
}
2.寫文件
-(void)applicationWillResignActive:(NSNotification *)notification{
NSString *filePath = [self dataFilePath];
NSArray *array = @[@"a",@"b",@"c",@"d"];//=[self.lineFields valueFoeKey:@"text"];
[array writeToFile:filePath atomically:YES];
}
//這個(gè)函數(shù)名字和形式比較特殊袜刷,只把它當(dāng)做一個(gè)滴啊有特殊功能的函數(shù)就好
//這個(gè)方法應(yīng)是作為observer的一個(gè)selector聪富,作用是:使應(yīng)用在進(jìn)入后臺(tái)或者終止運(yùn)行時(shí)保存數(shù)據(jù),只要應(yīng)用不在是前正在與用戶交互的應(yīng)用就會(huì)發(fā)布通知著蟹,例如不小心按了home鍵或者突然有電話打進(jìn)來
//獲取Document文件路徑
-(NSString)dataFilePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentDirectory = paths[0];
return [documentDirectory stringByAppendingPathComponent:@"data.plist"];//指定文件
//讀取tmp目錄
//NSString *tmpPath = NSTemporaryDirectory();
//NSString *tempFile = [tempPath stringByAppendingPathComponent:@"data.plist"];//體現(xiàn)出用的是“屬性列表”的方式
}
對(duì)象歸檔
#import <UIKit/UIKit.h>
//1.讀就是解碼的過程
-(void)viewDidLoad{
NSString *filePath = [self dataFilePath]
if( [[NSFileManager defaultManager] fileExistsAtPath:filePath] ){
//解碼通道
NSData *data = [[NSMutableData alloc] initWithContentOfFile:filePath];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData];
//根據(jù)編碼時(shí)設(shè)定的key找到要解碼的對(duì)象
MyObject *myObject = [unarchiver decodeObjectForKey:myObjectKey];
//
[unarchiver finishDecoding];
//READ
UILabel *label.text = myObject[ObjectAtIndex:somenumber]墩蔓;
}
//下面是與歸檔編解碼無關(guān)的部分,監(jiān)聽self萧豆,如果發(fā)生應(yīng)用終止運(yùn)行則及時(shí)保存數(shù)據(jù)
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
}
2.寫就是編碼的過程
-(void)applicationWillResignActive:(NSNotification *)notification{
//文件寫入的地點(diǎn)奸披,要有
NSString *filePath = [self dataFilePath];
//要保存的對(duì)象
MyObject *myObject = [[MyObject alloc]init];
myObject addObject:UILabel.text;//進(jìn)行一些賦值操作,這里label沒有正常聲明
//打開“通道”(不知道這樣形容合適不),必須要做的兩個(gè)步驟
NSMutableData *data = [[NSMutableData alloc]init];
NSKeyedArchiver *archiver = [[NSKeyArchiver alloc]initFoeWritingWithMutableData:data];
//建立鍵值關(guān)系,解碼時(shí)通過該key去找應(yīng)該解碼的對(duì)象
[archiver encodeObject:myObject forKey:myObjectKey];
//
[archiver finishEncoding];
//WRITE
[data writeToFile :filePath atomically:YES];
}
//獲取文件路徑
-(NSString)dataFilePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentDirectory = paths[0];
return [documentDirectory stringByAppendingPathComponent:@"data.plist"];//指定文件
return [documentDirectory stringByAppendingPathComponent:@"data.archive"]//體現(xiàn)出用的“歸檔”的方式
}
SQLite3
/*
NOTE:
1.SQL語句通過*char 傳遞而非NSString 兩者的相互轉(zhuǎn)換方式為:
const char *stringPath = [pathString UTF8String];
2.C語言小知識(shí):如果兩個(gè)內(nèi)聯(lián)字符串之間只有空白(或者換行符)炕横,而沒有自他字符源内,則兩個(gè)字符串將會(huì)被連接為一個(gè)字符串
*/
//基礎(chǔ)使用
//1.聲明和創(chuàng)建
sqlite3 *database;
int result = sqlite3_open("/path/to/database/file",&database);//result = SQLITE_OK if open database successfully
//2.執(zhí)行語句
//2-1用于updata insert delete等不反回任何數(shù)據(jù)的命令 —— sqlite3_exce
char *errorMsg;
const char *createSQL = "CREATE TABLE IF NOT EXISTS PEOPLE" "(ID INTEGER PROMARY KEY AUTOINCREMENT ,FIELD_DATA TEXT)";
int reslut = sqlite3_exec(database,createSQL,NULL,NULL,&errorMsg);//errorMsg將對(duì)錯(cuò)誤信息進(jìn)行描述
//2-2用于query檢索的命令 —— sqlite3_prepare_v2
NSString *query = @"SELECT ID , FIELD_DATA FROM FIELDS ORDER BY ROW";
sqlite3_stmt *statement;
int result = sqlite3_prepare_v2(database,[query UTF8String],-1,$statement,nil);
//3.取出數(shù)據(jù)
//4.使用綁定變量出入數(shù)據(jù)
char *sql = "insert into foo values (?,?); ";
sqlite3_stmt *stmt;
if(sqlite3_prepare_v2(database, sql,-1,&stmt,nil) == SQLITE_OK){
sqlite3_bind_int(stmt, 1, 235);
sqlite3_bind_text(stmt,2,"Text",-1份殿,NULL);
//("prepare語句的返回值" , "替代問號(hào)的索引" , "問號(hào)的替代內(nèi)容" , "上一個(gè)參數(shù)的傳入長度:-1代表字符串長度","可選用的內(nèi)存清理方式")
}
if(sqlite3_step(stmt)!= SQLITE_DONE){
NSLog(@"Real Error");
}
sqlite3_finalize(stmt);
程序中的讀膜钓、寫法
#import <UIKit/UIKit.h>
1.讀就是query檢所操作
-(void)viewDidLoad{
}
2.寫就是更新、插入卿嘲、刪除操作
//獲取文件路徑
-(NSString)dataFilePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentDirectory = paths[0];
return [documentDirectory stringByAppendingPathComponent:@"data.plist"];//指定文件
return [documentDirectory stringByAppendingPathComponent:@"data.archive"]//體現(xiàn)出用的“歸檔”的方式
return [documentDirectory stringByAppendingPathComponent:@"data.sqlite"]//體現(xiàn)出用SQLite3的方式
}
CoreData
概念
Core Data是水果推薦開發(fā)者使用的一種數(shù)據(jù)化持久方法颂斜,為了能夠更加準(zhǔn)確地理解,也是翻了不少前輩貢獻(xiàn)的資料拾枣,著實(shí)收貨很大
CoreData中的核心對(duì)象:
注:黑色表示類名沃疮,紅色表示類里面的一個(gè)屬性
1, Managed Object Model
Managed Object Model 是描述應(yīng)用程序的數(shù)據(jù)模型梅肤,這個(gè)模型包含實(shí)體(Entity)司蔬,特性(Property),讀取請(qǐng)求(Fetch Request)等姨蝴。(下文都使用英文術(shù)語俊啼。)
2,Managed Object Context
Managed Object Context 參與對(duì)數(shù)據(jù)對(duì)象(Managed Object)進(jìn)行各種操作的全過程左医,并監(jiān)測數(shù)據(jù)對(duì)象的變化授帕,以提供對(duì) undo/redo 的支持及更新綁定到數(shù)據(jù)的 UI同木。
3,Persistent Store Coordinator
Persistent Store Coordinator 相當(dāng)于數(shù)據(jù)文件管理器跛十,處理底層的對(duì)數(shù)據(jù)文件的讀取與寫入彤路。一般我們無需與它打交道。在context的操作下芥映,負(fù)責(zé)在數(shù)據(jù)庫中生成取出managed object洲尊,或者將managed Object存到數(shù)據(jù)庫
4,Managed Object
Managed Object 數(shù)據(jù)對(duì)象屏轰,與 Managed Object Context 相關(guān)聯(lián)颊郎。
關(guān)于Model
模型有點(diǎn)像數(shù)據(jù)庫的表結(jié)構(gòu),里面包含 Entry霎苗, 實(shí)體又包含三種 Property:Attribute(屬性)姆吭,RelationShip(關(guān)系), Fetched Property(讀取屬性)唁盏。Model class 的名字多以 "Description" 結(jié)尾内狸。我們可以看出:模型就是描述數(shù)據(jù)類型以及其關(guān)系的。
主要的 Model class 有:
First Header | Second Header | Third Header |
---|---|---|
Managed Object Model | NSManagedObjectModel | 數(shù)據(jù)模型 |
Entity | NSEntityDescription | 抽象數(shù)據(jù)類型厘擂,相當(dāng)于數(shù)據(jù)庫中的表 |
Property | NSPropertyDescription | Entity 特性昆淡,相當(dāng)于數(shù)據(jù)庫表中的一列 |
Attribute | NSAttributeDescription | 基本數(shù)值型屬性(如Int16, BOOL, Date等類型的屬性) |
Relationship | NSRelationshipDescription | 屬性之間的關(guān)系 |
Fetched Property | NSFetchedPropertyDescription | 查詢屬性(相當(dāng)于數(shù)據(jù)庫中的查詢語句) |
和數(shù)據(jù)庫的對(duì)應(yīng)關(guān)系
Manged Object <------> Database
Entity <-------> Table
Property <-------> 列 : 包含 Attribute、Relationship刽严、Fetched Property
開發(fā)步驟總結(jié):
- 初始化NSManagedObjectModel對(duì)象昂灵,加載模型文件,讀取app中的所有實(shí)體信息
- 初始化NSPersistentStoreCoordinator對(duì)象舞萄,添加持久化庫(這里采取SQLite數(shù)據(jù)庫)
- 初始化NSManagedObjectContext對(duì)象眨补,拿到這個(gè)上下文對(duì)象操作實(shí)體,進(jìn)行CRUD操作
這幾個(gè)對(duì)象用通俗的話這樣理解:(并且按照下面的順序理解)
- NSManagedObjectModel 確定和哪個(gè)應(yīng)用連接倒脓,就是說撑螺,這個(gè)Core Data是針對(duì)哪個(gè)app設(shè)計(jì)的
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"NewsModel" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
2 . NSPersistentStoreCoordinator
字面意就是持久化存儲(chǔ)器,所以崎弃,這個(gè)對(duì)象作用就是定位數(shù)據(jù)庫甘晤,就是說,我這個(gè)應(yīng)用用的是哪個(gè)數(shù)據(jù)庫饲做,But~~還要指明是哪個(gè)應(yīng)用對(duì)吧线婚,哪個(gè)應(yīng)用?這在上面的self.managedObjectModel
已經(jīng)確定了盆均,所以把managedObjectModel
引入就可以了酌伊。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"NewsModel.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
3 . NSManagedObjectContext
程序中負(fù)責(zé)操作數(shù)據(jù)的接口提供者,就是說缀踪,用這個(gè)對(duì)象的實(shí)例去CRUD居砖,這之前要指明該對(duì)象的實(shí)例用的是哪個(gè)持久化存儲(chǔ)器persistentStore
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
增刪查改
//插入數(shù)據(jù)
- (void)insertCoreData:(NSMutableArray*)dataArray
{
NSManagedObjectContext *context = [self managedObjectContext]; //都要聲明這個(gè)負(fù)責(zé)上下文連接的實(shí)例
for (News *info in dataArray) {
News *newsInfo = [NSEntityDescription insertNewObjectForEntityForName:TableName inManagedObjectContext:context];
newsInfo.newsid = info.newsid;
newsInfo.title = info.title;
newsInfo.imgurl = info.imgurl;
newsInfo.descr = info.descr;
newsInfo.islook = info.islook;
NSError *error;
if(![context save:&error])
{
NSLog(@"不能保存:%@",[error localizedDescription]);
}
}
}
//查詢
- (NSMutableArray*)selectData:(int)pageSize andOffset:(int)currentPage
{
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:TableName inManagedObjectContext:context];
//setFetchLimit
//setFetchOffset
//查找指定日期之后創(chuàng)建的
NSPredicate * predicate;
predicate = [NSPredicate predicateWithFormat:@"creationDate > %@", date];
//將結(jié)果按照title排序
NSSortDescriptor * sort = [[NSortDescriptor alloc] initWithKey:@"title"];
NSArray * sortDescriptors = [NSArray arrayWithObject: sort];
[fetchRequest setEntity:entity];
[fetchRequest setFetchLimit:pageSize]; // 限定查詢結(jié)果的數(shù)量
[fetchRequest setFetchOffset:currentPage]; // 查詢的偏移量
[fetchRequest setPredicted:predicate]; // 設(shè)置查詢條件
[fetchRequest setSortDescriptors:sort]; // 設(shè)置查詢結(jié)果的排序方法
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
NSMutableArray *resultArray = [NSMutableArray array];
for (News *info in fetchedObjects) {
NSLog(@"id:%@", info.newsid);
NSLog(@"title:%@", info.title);
[resultArray addObject:info];
}
return resultArray;
}
//刪除
-(void)deleteData
{
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:TableName inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setIncludesPropertyValues:NO];
[request setEntity:entity];
NSError *error = nil;
NSArray *datas = [context executeFetchRequest:request error:&error];
if (!error && datas && [datas count])
{
for (NSManagedObject *obj in datas)
{
[context deleteObject:obj];
}
if (![context save:&error])
{
NSLog(@"error:%@",error);
}
}
}
//更新
- (void)updateData:(NSString*)newsId withIsLook:(NSString*)islook
{
NSManagedObjectContext *context = [self managedObjectContext];
NSPredicate *predicate = [NSPredicate
predicateWithFormat:@"newsid like[cd] %@",newsId];
//首先你需要建立一個(gè)request
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:TableName inManagedObjectContext:context]];
[request setPredicate:predicate];//這里相當(dāng)于sqlite中的查詢條件,具體格式參考蘋果文檔
//https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pCreating.html
NSError *error = nil;
NSArray *result = [context executeFetchRequest:request error:&error];//這里獲取到的是一個(gè)數(shù)組驴娃,你需要取出你要更新的那個(gè)obj
for (News *info in result) {
info.islook = islook;
}
//保存
if ([context save:&error]) {
//更新成功
NSLog(@"更新成功");
}
最后
關(guān)于幾個(gè)持久化方法奏候,有幾個(gè)點(diǎn)需要注意一下:
- 使用 屬性列表 和 歸檔 還需要考慮將數(shù)據(jù)存在一個(gè)文件還是多個(gè)文件中,即單文件持久化(多用)唇敞、多文件持久化蔗草;
- 在寫文件是用到[myArray writeToFile:filePath atomically:YES] 第二個(gè)參數(shù)是叫數(shù)據(jù)寫入輔助文件,寫完后再將輔助文件復(fù)制到第一個(gè)參數(shù)指明的位置疆柔,保障寫安全性咒精;
- 采用屬性列表法無法序列化自定義對(duì)象,且無法序列化NSURL旷档,UIImage模叙,UIColor等類;
- 歸檔能夠大多數(shù)類進(jìn)行編解碼來實(shí)現(xiàn)數(shù)據(jù)持久化鞋屈,只要類中的每個(gè)屬性都是標(biāo)量或者遵循NSCoding某各類的實(shí)例范咨,都可以歸檔;
done厂庇!