1.簡(jiǎn)介
數(shù)據(jù)持久存儲(chǔ)是一種非易失性存儲(chǔ)胳搞,在重啟動(dòng)計(jì)算機(jī)或設(shè)備后也不會(huì)丟失數(shù)據(jù)绷落。持久化技術(shù)主要用于MVC模型中的model層姥闪。其中目前再IOS平臺(tái)上主要使用如下的四種技術(shù):
- 屬性列表
- 對(duì)象歸檔
- SQLite3
- Core Data
2.沙盒(SandBox)
IOS中的沙盒機(jī)制(SandBox)是一種安全體系,它規(guī)定了應(yīng)用程序只能在為該應(yīng)用創(chuàng)建的文件夾內(nèi)讀取文件砌烁,不可以訪問(wèn)其他地方的內(nèi)容筐喳。所有的非代碼文件都保存在這個(gè)地方,比如圖片函喉、聲音避归、屬性列表和文本文件等。
沙盒結(jié)構(gòu)
每個(gè)應(yīng)用程序沙盒主要包含以下三個(gè)目錄:
【注】:也包含其他目錄管呵,但不設(shè)計(jì)數(shù)據(jù)持久化梳毙。比如:AppName.app 目錄:這是應(yīng)用程序的程序包目錄,包含應(yīng)用程序的本身捐下。由于應(yīng)用程序必須經(jīng)過(guò)簽名账锹,所以您在運(yùn)行時(shí)不能對(duì)這個(gè)目錄中的內(nèi)容進(jìn)行修改,否則可能會(huì)使應(yīng)用程序無(wú)法啟動(dòng)坷襟。
****1) Documents:****
????應(yīng)用程序可以將數(shù)據(jù)存儲(chǔ)在Documents目錄中奸柬。在此目錄中的文件可以被共享,其中本文中的4種數(shù)據(jù)持久化技術(shù)都涉及該目錄啤握。
????****2) Library:****
????這個(gè)目錄下有兩個(gè)子目錄: ****Caches 和 Preferences ****
????****① Preferences**** 目錄:包含應(yīng)用程序的偏好設(shè)置文件鸟缕。您不應(yīng)該直接創(chuàng)建偏好設(shè)置文件,而是應(yīng)該使用NSUserDefaults類來(lái)取得和設(shè)置應(yīng)用程序的偏好。
????****② Caches**** 目錄:用于存放應(yīng)用程序?qū)S玫闹С治募樱4鎽?yīng)用程序再次啟動(dòng)過(guò)程中需要的信息授段。
????****3)Tmp:****
????Tmp目錄供應(yīng)用存儲(chǔ)臨時(shí)文件。在不需要這些文件時(shí)番甩,應(yīng)用要負(fù)責(zé)刪除tmp中等待文件侵贵,以免占用文件系統(tǒng)的空間。
獲取目錄
獲取沙盒主路徑:NSHomeDirectory()
獲取Document路徑:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]
獲取Library路徑:[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]
ps:第一個(gè)參數(shù)指明要查找的內(nèi)容缘薛,第二參數(shù)指明查找的范圍窍育,其中返回值說(shuō)明返回的是數(shù)組,但是由于在沙盒中只有一個(gè)documents宴胧,或是只有一個(gè)library漱抓,那么返回的數(shù)組只有一個(gè)元素,我們只需取得第一個(gè)元素即可恕齐。
獲取Temp路徑:NSTemporaryDirectory( )
獲取應(yīng)用包路徑:[[NSBundle mainBundle] pathForAuxiliaryExecutable:@”“]
3.NSFileManager 文件管理類
1)NSFileManager可以完成沙盒路徑下的文件管理工作乞娄,包括目錄創(chuàng)建、文件創(chuàng)建显歧、刪除仪或、移動(dòng)、復(fù)制等士骤。
2)NSFileManager使用單例方法訪問(wèn):
??NSFileManager *fileManager = [NSFileManager defaultManager];
3)判斷指定路徑下是否存在文件:
- (BOOL)fileExistsAtPath:(NSString *)path;
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory;
4)文件處理方法:
// 1)創(chuàng)建文件
- (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data attributes:(NSDictionary *)attr;
// 2)創(chuàng)建目錄
- (BOOL)createDirectoryAtPath:(NSString *)path attributes:(NSDictionary *)attributes;
// 3)復(fù)制文件
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;
// 4)移動(dòng)文件
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;
// 5)刪除文件
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError *)error;
4范删、NSUserDefaults(偏好設(shè)置)
NSUserDefaults單例以key-value的形式存儲(chǔ)了一系列偏好設(shè)置,key是名稱拷肌,value是相應(yīng)的數(shù)據(jù)到旦。存取數(shù)據(jù)時(shí)可以使用方法objectForKey:和setObject:forKey:來(lái)把對(duì)象存儲(chǔ)到相應(yīng)的plist文件中,或者讀取廓块,既然是plist文件厢绝,那么對(duì)象的類型則必須是plist文件可以存儲(chǔ)的類型:
NSNumber(NSInteger、float带猴、double)
NSString
NSDate
NSArray
NSDictionary
BOOL
1)NSUserDefaults 存儲(chǔ)數(shù)據(jù):
- (void)setObject:(id)value forKey:(NSString *)defaultName;
- (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName;
- (void)setFloat:(float)value forKey:(NSString *)defaultName;
- (void)setDouble:(double)value forKey:(NSString *)defaultName;
- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
- (void)setURL:(NSURL *)url forKey:(NSString *)defaultName;
2)NSUserDefaults 讀取數(shù)據(jù):
- (id)objectForKey:(NSString *)defaultName;
- (NSInteger)integerForKey:(NSString *)defaultName;
- (float)floatForKey:(NSString *)defaultName;
- (double)doubleForKey:(NSString *)defaultName;
- (BOOL)boolForKey:(NSString *)defaultName;
- (NSURL *)URLForKey:(NSString *)defaultName;
3)NSUserDefaults同步數(shù)據(jù)到文件系統(tǒng):
- (BOOL)synchronize;
代碼示例:
// 如果想要將數(shù)據(jù)永久保存到NSUserDefaults中,代碼實(shí)現(xiàn)為:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@”li“ forKey:@"name"];
[defaults setInteger:10 forKey:@"age"];
[defaults synchronize];
ps:其中懈万,方法synchronise是為了強(qiáng)制存儲(chǔ)拴清,其實(shí)并非必要,因?yàn)檫@個(gè)方法會(huì)在系統(tǒng)中默認(rèn)調(diào)用会通,但是你確認(rèn)需要馬上就存儲(chǔ)口予,這樣做是可行的。
// 將數(shù)據(jù)取出涕侈,只需要取出key 對(duì)應(yīng)的值就好了沪停,代碼如下:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [defaults objectForKey:@"name"]
NSInteger age = [defaults integerForKey:@"age"];
5、屬性列表
屬性列表文件是一種xml文件,拓展名為plist(Property List)木张,F(xiàn)oundation框架中的數(shù)組和字典都可以與屬性列表文件互相轉(zhuǎn)換众辨。簡(jiǎn)單的說(shuō)就是調(diào)用數(shù)組或字典的方法(read或write)進(jìn)行xml文件的讀或?qū)懖僮鳌?br> 雖然可以將數(shù)組和字典轉(zhuǎn)換為XML文件,但只有某些對(duì)象才能被放置到集合(即數(shù)組和字典)中舷礼,來(lái)實(shí)現(xiàn)轉(zhuǎn)換鹃彻。這些可被放置到集合的類有如下:
Array、NSArray妻献、NSMutableArray蛛株;
Dictionary、NSDictionary育拨、NSMutableDictionary谨履;
NSData、NSMutableData熬丧;
String屉符、NSString、NSMutableString锹引;
NSNumber矗钟;
NSDate
集合 | 方法(Object-C) | 描述 |
---|---|---|
NSArray |
+arrayWithContentsOfFile(讀) |
靜態(tài)創(chuàng)建工廠方法,用于從屬性列表文件中讀取數(shù)據(jù)嫌变,創(chuàng)建 NSArray對(duì)象吨艇。Swift沒(méi)有對(duì)應(yīng)的構(gòu)造器。 |
NSArray |
initWithContentsOfFile(讀) |
構(gòu)造器腾啥,用于從屬性列表文件中讀取數(shù)據(jù)东涡,創(chuàng)建NSArray對(duì)象。Swift表示為convenience init?(contentsOfFile aPath:String)倘待。 |
NSArray |
-writeToFile:atomically(寫(xiě)) |
該方法把NSArray對(duì)象寫(xiě)入屬性列表文件中疮跑。Swift是writeToFile。 |
NSDictionary |
+dictionaryWithContentsOfFile(讀) |
靜態(tài)工廠方法凸舵,從屬性列表文件中讀取數(shù)據(jù)祖娘,創(chuàng)建NSDictionary對(duì)象。Swift沒(méi)有對(duì)應(yīng)的構(gòu)造器啊奄。 |
NSDictionary |
-initWithContentsOfFile(讀) |
構(gòu)造器渐苏,從屬性列表文件中讀取數(shù)據(jù),創(chuàng)建NSDictionary對(duì)象菇夸。Swift表示成convenience init?(contentsOfFile aPath:String)琼富。 |
NSDictionary |
-writeToFile:atomically(寫(xiě)) |
將NSDictionary對(duì)象寫(xiě)入到屬性列表文件中,Swift是writeToFile庄新。 |
【注】:由于Swift代碼中的writeToFile(toFile:鞠眉,atomically:)方法實(shí)際屬于ObjectC的NSArray或NSDictionary類薯鼠。所以要使用這個(gè)方法時(shí),需要將Swift的Array(Dictionary)強(qiáng)制轉(zhuǎn)換為NSArray(NSDictionary)械蹋。如:
let nsArray = array as! NSArray
代碼示例:
// 用swift簡(jiǎn)寫(xiě)一下代碼的執(zhí)行邏輯出皇,至于怎么優(yōu)化格式封裝方式這里不做說(shuō)明.
class ViewController: UIViewController {
open func create() {
// 創(chuàng)建swift的數(shù)組
let array:[String] = ["1","2","3"];
// 將swift的數(shù)組轉(zhuǎn)換為ObjectC的數(shù)組,并將數(shù)組寫(xiě)入屬性列表文件
let writeArray:NSArray = array as NSArray;
// 寫(xiě)入到屬性列表文件中
writeArray.write(toFile: self.applicationDocumentsDirectoryFile(), atomically: true);
// 從屬性列表文件中讀取數(shù)組
let readArray = NSArray(contentsOfFile: self.applicationDocumentsDirectoryFile()) as! [String];
// 輸出驗(yàn)證
for str in readArray {
print(str);
}
}
// 獲得數(shù)據(jù)庫(kù)文件路徑
func applicationDocumentsDirectoryFile() -> String {
let documentDirectory: NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
let path = (documentDirectory[0] as AnyObject).appendingPathComponent("NotesList.plist") as String
print("path : \(path)")
return path
}
// 【注】:這里用的時(shí)候直接self.朝蜘,每次都要get一下翘瓮,在真正項(xiàng)目中盡量不要這樣調(diào)用媒殉,可以設(shè)置一個(gè)私有的沙箱目錄中屬性列表文件路徑。
// fileprivate var plistFilePath: String!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 調(diào)用函數(shù)
self.create()
}
}
6、對(duì)象歸檔
歸檔與屬性列表方式不同窃页,屬性列表只有指定的一些對(duì)象才能進(jìn)行持久化晌杰,而歸檔是任何實(shí)現(xiàn)了NSCopying協(xié)議的對(duì)象都可以被持久化翼悴,其中歸檔涉及兩個(gè)類:NSKeyedArchiver和NSKeyedUnarchiver刁赦。歸檔和反歸檔都是采用健值對(duì)的形式編碼。
實(shí)現(xiàn)協(xié)議:
方法 | 描述 |
---|---|
-(void)encodeWithCoder:(NSCoder *)encoder |
對(duì)象進(jìn)行序列化的方法煮剧,把對(duì)象信息封裝在NSCoder對(duì)象中斥滤。 |
-(instancetype)initWithCoder:(NSCoder *)decoder |
對(duì)象的反序列化方法,通過(guò)NSCoder對(duì)象獲取相應(yīng)數(shù)據(jù)勉盅。 |
其中encoder和decoder是提供給用戶進(jìn)行編碼和解碼的流對(duì)象佑颇,兩個(gè)都是采用健值對(duì)的形式進(jìn)行操作,并根據(jù)不同的數(shù)據(jù)類型提供不同的寫(xiě)入和讀取的方法草娜,如encodeInt挑胸、encodeFloat、decodeIntForKey和decodeFloatForKey等方法宰闰。
如下是Note類實(shí)現(xiàn)的兩個(gè)協(xié)議的程序:
@interface Note : NSObject<NSCoding>
@property(nonatomic, strong) NSDate *date;
@property(nonatomic, strong) NSString *content;
@end
------m------
@implementation Note
@synthesize date = _date;
@synthesize content = _content;
// - 歸檔(也叫序列化)
// - 當(dāng)將一個(gè)自定義對(duì)象保存到文件的時(shí)候就會(huì)調(diào)用該方法茬贵;
// - 在該方法中,說(shuō)明如何存儲(chǔ)自定義對(duì)象的屬性,也就是說(shuō)在該方法中說(shuō)清楚存儲(chǔ)自定義對(duì)象的哪些屬性移袍;
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_date forKey:@"date"];
[aCoder encodeObject:_content forKey:@"content"];
}
// - 反歸檔(也叫反序列化)
// - 當(dāng)文件中讀取一個(gè)對(duì)象的時(shí)候就會(huì)調(diào)用該方法解藻;
// - 在該方法中說(shuō)明如何讀取保存在文件中的對(duì)象,也就是說(shuō)在該方法中說(shuō)清楚怎么讀取文件中的對(duì)象
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self=[super init]) {
self.date = [aDecoder decodeObjectForKey:@"date"];
self.content = [aDecoder decodeObjectForKey:@"content"];
}
return self;
}
@end
【注】:要將一個(gè)自定義的類進(jìn)行歸檔,那么類里面的每個(gè)屬性都必須是可以被歸檔的葡盗,如果是不能歸檔的類型螟左,我們可以把他轉(zhuǎn)化為NSValue進(jìn)行歸檔,然后在讀出來(lái)的時(shí)候在轉(zhuǎn)化為相應(yīng)的類戳粒。通過(guò)****plist保存的數(shù)據(jù)是直接顯示的****路狮,不安全。通過(guò)歸檔方法保存的數(shù)據(jù)在文件中打開(kāi)是亂碼的蔚约,****加密的****,更安全涂籽。
歸檔與反歸檔
****所謂歸檔:****將復(fù)雜對(duì)象轉(zhuǎn)化為NSData類型數(shù)據(jù)(復(fù)雜-->歸檔-->NSData--->WriteToFile)
注意:歸檔是將對(duì)象轉(zhuǎn)化為數(shù)據(jù)字節(jié),以文件的形式存儲(chǔ)在磁盤上
歸檔化過(guò)程是使用NSKeyedArchiver對(duì)象歸檔數(shù)據(jù)苹祟,其操作步驟如下: |
---|
1) 創(chuàng)建NSMutableData對(duì)象:只需使用構(gòu)造函數(shù)init()創(chuàng)建為空的對(duì)象; |
2) 創(chuàng)建NSKeyedArchiver對(duì)象:用其構(gòu)造函數(shù)initForWritingWithMutableData()創(chuàng)建對(duì)象; |
3) 歸檔對(duì)象:調(diào)用NSKeyedArchiver對(duì)象的encodeObject()方法寫(xiě)入被歸檔的對(duì)象树枫; |
4) 完成操作:調(diào)用NSKeyedArchiver對(duì)象的finishEncoding()方法完成寫(xiě)入操作直焙; |
5) 寫(xiě)入文件:調(diào)用NSMutableData對(duì)象的writeToFile()寫(xiě)入到指定的目錄下; |
****所謂反歸檔:****將NSData類型數(shù)據(jù)轉(zhuǎn)化為復(fù)雜對(duì)象(讀取文件-->NSData-->反歸檔--->復(fù)雜對(duì)象)
對(duì)象反歸檔的過(guò)程與對(duì)象歸檔過(guò)程類似砂轻,不同的是在創(chuàng)建NSMutableData對(duì)象時(shí)奔誓,需要指定目錄路徑,且不需要寫(xiě)入文件中搔涝。其操作步驟如下: |
---|
1) 創(chuàng)建NSMutableData對(duì)象:指定文件路徑調(diào)用構(gòu)造函數(shù)initWithContentsOfFile()創(chuàng)建對(duì)象厨喂; |
2) 創(chuàng)建NSKeyedUnarchiver 對(duì)象:用其構(gòu)造函數(shù)initForReadingWithData()創(chuàng)建對(duì)象; |
3) 反歸檔對(duì)象:調(diào)用NSKeyedUnarchiver 對(duì)象的decodeObjectForKey()方法寫(xiě)入被歸檔的對(duì)象庄呈; |
4) 完成操作:調(diào)用NSKeyedUnarchiver 對(duì)象的finishEncoding()方法完成寫(xiě)入操作蜕煌; |
代碼示例:
#define FILE_NAME @"NotesList.archive"
#define ARCHIVE_KEY @"NotesList"
// 獲取Documents的路徑,并創(chuàng)建NotesList.archive的路徑
- (NSString *)applicationDocumentsDirectoryFile {
NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [documentDirectory stringByAppendingPathComponent:FILE_NAME];
NSLog(@"路徑:%@", path);//Documents
return path;
}
// 創(chuàng)建被保存的數(shù)據(jù)對(duì)象
Note *note = [[Note alloc] init];
note.date = [[NSDate alloc] init];
note.content = @"li'xiang";
NSString *path = [self applicationDocumentsDirectoryFile];
// 設(shè)置數(shù)據(jù)區(qū)诬留,并將其連接到一個(gè)NSKeyedArchiver對(duì)象
NSMutableData *theData = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:theData];
// 進(jìn)行數(shù)據(jù)歸檔
[archiver encodeObject:array forKey:ARCHIVE_KEY];
// 發(fā)出歸檔完成消息
[archiver finishEncoding];
// 將存檔的數(shù)據(jù)區(qū)寫(xiě)入文件中斜纪。
[theData writeToFile:path atomically:YES];
// 進(jìn)行數(shù)據(jù)反歸檔
NSString *path = [self applicationDocumentsDirectoryFile];
NSData *theData = [NSData dataWithContentsOfFile:path];
NSKeyedUnarchiver *archiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData:theData];
Note *listData = [archiver decodeObjectForKey:ARCHIVE_KEY];
// 結(jié)束反歸檔
[archiver finishDecoding];
編碼和反編碼C語(yǔ)言類型
NSKeyedArchiver 和NSKeyedUnarchiver類不能對(duì)structures, arrays, 和bit fields類型進(jìn)行編碼或反編碼。
1)指針類型
由于不能歸檔指針類型文兑,所以若需要只能歸檔指針?biāo)赶虻膶?duì)象盒刚。但對(duì)于C語(yǔ)言的字符串類型(char*)卻是支持的,它比較特殊绿贞,可以使用 encodeBytes:length:forKey:方法進(jìn)行歸檔因块。
2)基本數(shù)據(jù)類型的數(shù)組
一種基本的方法是一個(gè)元素一個(gè)元素歸檔,如"theArray[0]", "theArray[1]"這樣一個(gè)個(gè)的進(jìn)行歸檔樟蠕,非常簡(jiǎn)單贮聂。
3)對(duì)象類型的數(shù)組
對(duì)于C語(yǔ)言的數(shù)組且元素類型是對(duì)象,那么最簡(jiǎn)單的方式是將該數(shù)組用NSArray進(jìn)行封裝寨辩。這樣就可以進(jìn)行歸檔了吓懈;當(dāng)進(jìn)行反歸檔時(shí),也是獲得NSArray對(duì)象靡狞,然后一個(gè)個(gè)地拆封為C語(yǔ)言的數(shù)組元素耻警。
4)數(shù)據(jù)結(jié)構(gòu)類型
可以將結(jié)構(gòu)體的封裝為一個(gè)對(duì)象,對(duì)象的每個(gè)成員是結(jié)構(gòu)體的每個(gè)成員甸怕。若需要?dú)w檔則先將結(jié)構(gòu)體封裝為OC對(duì)象甘穿,然后再歸檔;而反歸檔則是將其解析為OC對(duì)象梢杭,然后轉(zhuǎn)換為C語(yǔ)言結(jié)構(gòu)體温兼。
7、SQLite3
SQLite是目前主流的第三方的數(shù)據(jù)庫(kù)嵌入式關(guān)系型數(shù)據(jù)庫(kù)武契,其最主要的特點(diǎn)就是輕量級(jí)募判、跨平臺(tái)荡含,當(dāng)前很多嵌入式都將其作為數(shù)據(jù)庫(kù)首選。SQLite提供多種方式的接口届垫,有命令行的接口释液、客戶端及多種語(yǔ)言的API接口。在iOS中需要使用C語(yǔ)言語(yǔ)法進(jìn)行數(shù)據(jù)庫(kù)操作装处、訪問(wèn)(無(wú)法使用ObjC直接訪問(wèn)误债,因?yàn)閘ibqlite3框架基于C語(yǔ)言編寫(xiě))。
配置
在項(xiàng)目中的Build Phases選項(xiàng)中的Link Binary With Libraries下妄迁,點(diǎn)擊其"+"號(hào)寝蹈,從而添加所需要的libsqlite3.tbd或者是libsqlite3.0.tbd庫(kù)。
在需要操作sqlite數(shù)據(jù)的文件中導(dǎo)入如下頭文件:
oc直接添加#import "sqlite3.h"
swift添加橋接文件判族,新建一個(gè).h文件躺盛,在里邊添加#import "SQLite3.h"
然后在進(jìn)入Buil Setting
選項(xiàng)中,搜索Bridging Header
形帮,雙擊添加相對(duì)路徑:$(SRCROOT)/文件在工程下路徑槽惫,如在主目錄這里刪除/SQLite_Bridge.h
【注】:實(shí)際上libsqlite3.0.tbd本身是個(gè)替身,它指向libsqlite3.tbd辩撑。也就是說(shuō)在項(xiàng)目里如果你添加libsqlite3.tbd和添加libsqlite3.0.tbd其實(shí)是添加了同一個(gè)文件界斜,如果引用的是libsqlite3.0.tbd,SQLite庫(kù)更新項(xiàng)目中不必修改了合冀。
方法 | 描述 |
---|---|
sqlite3 *db |
數(shù)據(jù)庫(kù)句柄各薇,跟文件句柄FILE很類似 |
sqlite3_stmt *stmt |
這個(gè)相當(dāng)于ODBC的Command對(duì)象,用于保存編譯好的SQL語(yǔ)句 |
sqlite3_open() |
打開(kāi)數(shù)據(jù)庫(kù)君躺,沒(méi)有數(shù)據(jù)庫(kù)時(shí)創(chuàng)建 |
sqlite3_exec() |
執(zhí)行非查詢的sql語(yǔ)句 |
sqlite3_step() |
在調(diào)用sqlite3_prepare后峭判,使用這個(gè)函數(shù)在記錄集中移動(dòng) |
sqlite3_close() |
關(guān)閉數(shù)據(jù)庫(kù)文件 |
sqlite3_column_text() |
取text類型的數(shù)據(jù) |
sqlite3_column_blob() |
取blob類型的數(shù)據(jù) |
sqlite3_column_int() |
取int類型的數(shù)據(jù) |
代碼示例:
OC實(shí)在沒(méi)啥可寫(xiě)的,這里就用swift簡(jiǎn)單寫(xiě)一下邏輯棕叫,詳細(xì)的就不多提了林螃。
class ViewController: UIViewController {
// 定義數(shù)據(jù)庫(kù)變量
var db:OpaquePointer? = nil //sqlite3 *db
// 查詢數(shù)據(jù)方法
public func findAll() {
let path = self.applicationDocumentsDirectoryFile()
let cpath = path.cString(using: String.Encoding.utf8)
var statement:OpaquePointer? = nil
if sqlite3_open(cpath!, &db) != SQLITE_OK {
// 數(shù)據(jù)庫(kù)打開(kāi)失敗。
NSLog("數(shù)據(jù)庫(kù)打開(kāi)失敗俺泣。")
} else {
let sql = "SELECT cdate,content FROM Note"
let cSql = sql.cString(using: String.Encoding.utf8)
//預(yù)處理過(guò)程
if sqlite3_prepare_v2(db, cSql!, -1, &statement, nil) == SQLITE_OK {
// 執(zhí)行
while sqlite3_step(statement) == SQLITE_ROW {
if let strContent = getColumnValue(index:0, stmt:statement!) {
NSLog("數(shù)據(jù)打印:%@", strContent)
}
}
}
}
defer {
print("關(guān)閉數(shù)據(jù)庫(kù)")
sqlite3_close(db)
}
defer {
print("釋放語(yǔ)句對(duì)象")
sqlite3_finalize(statement)
}
}
// 插入數(shù)據(jù)與查詢數(shù)據(jù)
public func create() {
let path = self.applicationDocumentsDirectoryFile()
let cpath = path.cString(using: String.Encoding.utf8)
var statement: OpaquePointer? = nil
if sqlite3_open(cpath!, &db) != SQLITE_OK {
//數(shù)據(jù)庫(kù)打開(kāi)失敗疗认。
NSLog("數(shù)據(jù)庫(kù)打開(kāi)失敗。")
} else {
let sql = "INSERT OR REPLACE INTO note (cdate, content) VALUES (?,?)"
let cSql = sql.cString(using: String.Encoding.utf8)
// 預(yù)處理過(guò)程
if sqlite3_prepare_v2(db, cSql!, -1, &statement, nil) == SQLITE_OK {
let cContent = "lixiang".cString(using: String.Encoding.utf8)
// 綁定參數(shù)開(kāi)始
sqlite3_bind_text(statement, 1, cContent!, -1, nil)
// 執(zhí)行插入
if sqlite3_step(statement) != SQLITE_DONE {
// 插入數(shù)據(jù)失敗伏钠。
NSLog("插入數(shù)據(jù)失敗横漏。")
}
}
}
defer {
print("釋放語(yǔ)句對(duì)象")
sqlite3_finalize(statement)
}
defer {
print("關(guān)閉數(shù)據(jù)庫(kù)")
sqlite3_close(db)
}
}
// 獲得字段數(shù)據(jù)
private func getColumnValue(index: CInt, stmt: OpaquePointer)->String? {
if let ptr = UnsafeRawPointer.init(sqlite3_column_text(stmt, index)) {
let uptr = ptr.bindMemory(to:CChar.self, capacity:0)
let txt = String(validatingUTF8:uptr)
return txt
}
return nil
}
// 創(chuàng)建數(shù)據(jù)庫(kù)
func createEditableCopyOfDatabaseIfNeeded() {
let path = self.applicationDocumentsDirectoryFile()
let cpath = path.cString(using: String.Encoding.utf8)
// 第一個(gè)參數(shù):數(shù)據(jù)庫(kù)文件路徑 第二個(gè)參數(shù):數(shù)據(jù)庫(kù)對(duì)象
if sqlite3_open(cpath!, &db) != SQLITE_OK {
NSLog("數(shù)據(jù)庫(kù)打開(kāi)失敗。")
} else {
// 建表的SQL語(yǔ)句
let sql = "CREATE TABLE IF NOT EXISTS Note (cdate TEXT PRIMARY KEY, content TEXT)"
let cSql = sql.cString(using: String.Encoding.utf8)
if (sqlite3_exec(db,cSql!, nil, nil, nil) != SQLITE_OK) {
// 建表失敗熟掂。
NSLog("建表失敗缎浇。")
}
}
defer {
print("關(guān)閉數(shù)據(jù)庫(kù)")
sqlite3_close(db)
}
}
// 獲得數(shù)據(jù)庫(kù)文件路徑
private func applicationDocumentsDirectoryFile() ->String {
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
let path = (documentDirectory[0] as AnyObject).appendingPathComponent("NotesList.sqlite3") as String
print("path : \(path)")
return path
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 調(diào)用函數(shù)
self.createEditableCopyOfDatabaseIfNeeded()
self.create()
self.findAll()
}
}
ps:別忘了引入的時(shí)候創(chuàng)建橋接文件哦!
8、Core Data
Core Data是iOS的一個(gè)持久化框架赴肚,它提供了對(duì)象-關(guān)系映射(ORM)的功能华畏,即能夠?qū)⒊绦蛑械膶?duì)象(swift或Object-C中類的實(shí)例)轉(zhuǎn)化成數(shù)據(jù)鹏秋,保存在SQLite數(shù)據(jù)庫(kù)文件中尊蚁,也能夠?qū)⒈4嬖跀?shù)據(jù)庫(kù)中的數(shù)據(jù)還原成程序中的對(duì)象亡笑。在此數(shù)據(jù)操作期間,我們不需要編寫(xiě)任何SQL語(yǔ)句横朋。
左邊是關(guān)系模型仑乌,即數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)里面有張person表琴锭,person表里面有id晰甚、name、age三個(gè)字段决帖,而且有2條記錄厕九;右邊是對(duì)象模型,可以看到地回,有2個(gè)OC對(duì)象扁远;利用Core Data框架,我們就可以輕松地將數(shù)據(jù)庫(kù)里面的2條記錄轉(zhuǎn)換成2個(gè)OC對(duì)象刻像,也可以輕松地將2個(gè)OC對(duì)象保存到數(shù)據(jù)庫(kù)中畅买,變成2條表記錄,而且不用寫(xiě)一條SQL語(yǔ)句细睡。
Core Data搭建結(jié)構(gòu)
類 | 描述 |
---|---|
NSManagedObjectModel |
稱為被管理對(duì)象模型類谷羞,是系統(tǒng)中的"實(shí)體",與數(shù)據(jù)庫(kù)中的表對(duì)象對(duì)應(yīng)溜徙,可以了解為圖4中對(duì)象的結(jié)合湃缎,該模型是通過(guò)項(xiàng)目中的.xcdatamodeld文件進(jìn)行聲明的。 |
NSPersisntentStoreCoordinator |
稱為持久化存儲(chǔ)協(xié)調(diào)器類蠢壹,在持久化對(duì)象存儲(chǔ)之上提供了一個(gè)接口嗓违,可以把它考慮成為數(shù)據(jù)庫(kù)的連接。即相當(dāng)是SQLite數(shù)據(jù)庫(kù)中的SQLite3類型知残。 |
NSManageedObjectContext |
稱為被管理對(duì)象上下文類靠瞎,在上下文中可以查找、刪除和插入對(duì)象求妹,然后通過(guò)棧同步到持久化對(duì)象存儲(chǔ)乏盐,即相當(dāng)是SQLite數(shù)據(jù)庫(kù)中的語(yǔ)句(sqlite3_Stmt類型)。其中程序員主要使用該實(shí)例對(duì)象間接地與數(shù)據(jù)庫(kù)進(jìn)行交互制恍。 |
創(chuàng)建模型文件的過(guò)程:
創(chuàng)建模板:
1)創(chuàng)建項(xiàng)目時(shí)父能,勾選"Use core Data"的復(fù)選框方式創(chuàng)建。
2)如果創(chuàng)建工程時(shí)沒(méi)有創(chuàng)建净神,可以同過(guò)command+N何吝,的方式創(chuàng)建溉委。
添加實(shí)體:
以一對(duì)一雙向關(guān)聯(lián)的兩個(gè)實(shí)體為例,即Person中有Card屬性爱榕,Card中有Person屬性瓣喊。
人實(shí)體中有:姓名(年齡),年齡(age)黔酥,卡(身份證)三個(gè)屬性
卡實(shí)體中有:無(wú)(號(hào)碼)藻三,人(人)兩個(gè)屬性
為兩個(gè)實(shí)體添加實(shí)體屬性:
建立兩個(gè)實(shí)體的關(guān)系:
代碼示例:
1.搭建上下文環(huán)境:
// 從應(yīng)用程序包中加載模型文件
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
// 傳入模型對(duì)象棵帽,初始化NSPersistentStoreCoordinator
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// 構(gòu)建SQLite數(shù)據(jù)庫(kù)文件的路徑
NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)lastObject];
NSURL *url = [NSURL fileURLWithPath:[docs stringByAppendingPathComponent:@"person.data" ]];
// 添加持久化存儲(chǔ)庫(kù),這里使用SQLite作為存儲(chǔ)庫(kù)
NSError *error = nil;
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
if(store == nil) { // 直接拋異常
[NSException raise:@"添加數(shù)據(jù)庫(kù)錯(cuò)誤" 格式:@"%@",[error localizedDescription]];
}
// 初始化上下文渣玲,設(shè)置persistentStoreCoordinator屬性
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
context.persistentStoreCoordinator = psc;
2.添加數(shù)據(jù)到數(shù)據(jù)庫(kù):
// 傳入上下文逗概,創(chuàng)建一個(gè)人實(shí)體對(duì)象
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
// 設(shè)置人的簡(jiǎn)單屬性
[person setValue:@"XN" forKey:@"name"];
[person setValue:[NSNumber numberWithInt:24] forKey:@"age"];
// 傳入上下文,創(chuàng)建一張卡實(shí)體對(duì)象
NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
[card setValue:@"1234567890" forKey:@"no"];
// 設(shè)置人與卡之間的關(guān)聯(lián)關(guān)系
[person setValue:card forKey:@"card"];
// 利用上下文對(duì)象忘衍,將數(shù)據(jù)同步到持久化存儲(chǔ)庫(kù)
NSError *error = nil;
BOOL success = [上下文保存:&error];
if(!success) {
[NSException raise:@"訪問(wèn)數(shù)據(jù)庫(kù)錯(cuò)誤"格式:@"%@", [error localizedDescription]];
}
// 如果是想做更新操作:只要在更改了實(shí)體對(duì)象的屬性后調(diào)用[context save:&error]逾苫,就能將更改的數(shù)據(jù)同步到數(shù)據(jù)庫(kù)
3.從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù):
// 初始化一個(gè)查詢請(qǐng)求
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//設(shè)置要查詢的實(shí)體
request.entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
// 設(shè)置排序(按照年齡降序)
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:sort];
// 設(shè)置條件過(guò)濾(搜索名稱中包含字符串“Itcast-1”的記錄,注意:設(shè)置條件過(guò)濾時(shí)間淑履,數(shù)據(jù)庫(kù)SQL語(yǔ)句中的%要用*來(lái)代替隶垮,所以%Itcast-1%應(yīng)該寫(xiě)成* 1 *)
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like%@", @"* Itcast-1 *"];
request.predicate = 謂詞;
// 執(zhí)行請(qǐng)求
NSError *error = nil;
NSArray *objs = [context executeFetchRequest:request error:&error];
// 遍歷數(shù)據(jù)
for(NSManagedObject *obj in objs) {
NSLog(@"name =%@", [obj valueForKey:@"name"])
}
4.刪除數(shù)據(jù)庫(kù)中的數(shù)據(jù):
// 傳入需要?jiǎng)h除的實(shí)體對(duì)象
[context deleteObject:managedObject];
// 將結(jié)果同步到數(shù)據(jù)庫(kù)
NSError * error = nil;
[上下文保存:&error];
if(error) {
[NSException raise:@"刪除錯(cuò)誤" 格式:@"%@", [error localizedDescription]];
}
打開(kāi)CoreData的SQL語(yǔ)句輸出開(kāi)關(guān):
1.打開(kāi)產(chǎn)品,點(diǎn)擊EditScheme ...
2.點(diǎn)擊參數(shù)秘噪,在參數(shù)中啟動(dòng)中添加2項(xiàng)
1> -com.apple.CoreData.SQLDebug
2> 1
創(chuàng)建NSManagedObject的子類
默認(rèn)情況下狸吞,利用核心數(shù)據(jù)取出的實(shí)體都是NSManagedObject類型的,能夠利用鍵 - 值對(duì)來(lái)存取數(shù)據(jù)指煎。但是一般情況下蹋偏,實(shí)時(shí)在存取數(shù)據(jù)的基礎(chǔ)上,有時(shí)還需要添加一些業(yè)務(wù)方法來(lái)完成一些其他任務(wù)至壤,那么就必須創(chuàng)建NSManagedObject子的類威始,實(shí)體類。
代碼示例:
添加數(shù)據(jù):
Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
person.name = @"XN";
person.age = [NSNumber numberWithInt:24];
Card *card = [NSEntityDescription insertNewObjectForEntityForName:@”Card" inManagedObjectContext:context];
card.no = @”1234567890";
person.card = card;
// 最后調(diào)用[context save&error];保存數(shù)據(jù)
9像街、FMDB
iOS中原生的SQLite API在進(jìn)行數(shù)據(jù)存儲(chǔ)的時(shí)候黎棠,需要使用C語(yǔ)言中的函數(shù),操作比較麻煩镰绎。于是脓斩,就出現(xiàn)了一系列將SQLite API進(jìn)行封裝的庫(kù),FMDB畴栖。
FMDB是一款簡(jiǎn)潔随静、易用的封裝庫(kù)。它是對(duì)libsqlite3框架的封裝,用起來(lái)的步驟與SQLite使用類似燎猛,并且它對(duì)于多線程的并發(fā)操作進(jìn)行了處理恋捆,所以是線程安全的。
核心類
FMDatabase
FMDatabase對(duì)象就代表一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫(kù)重绷,用來(lái)執(zhí)行SQL語(yǔ)句
FMDatabase這個(gè)類是線程不安全的沸停,如果在多個(gè)線程中同時(shí)使用一個(gè)FMDatabase實(shí)例,會(huì)造成數(shù)據(jù)混亂等問(wèn)題
為了保證線程安全论寨,F(xiàn)MDB提供方便快捷的FMDatabaseQueue類
FMResultSet
使用FMDatabase執(zhí)行查詢后的結(jié)果集
FMDatabaseQueue
用于在多線程中執(zhí)行多個(gè)查詢或更新星立,它是線程安全的,解決多個(gè)線程同時(shí)訪問(wèn)同個(gè)表而導(dǎo)致的崩潰問(wèn)題葬凳,串行隊(duì)列,不支持(block塊內(nèi))串行嵌套任務(wù)執(zhí)行
FMDB使用步驟
下載FMDB文件,并將FMDB文件夾添加到項(xiàng)目中(也可使用CocoaPods導(dǎo)入)
導(dǎo)入libsqlite3.0框架室奏,導(dǎo)入頭文件FMDatabase.h
代碼實(shí)現(xiàn)火焰,與SQLite使用步驟相似,創(chuàng)建數(shù)據(jù)庫(kù)路徑胧沫,獲得數(shù)據(jù)庫(kù)路徑昌简,打開(kāi)數(shù)據(jù)庫(kù),然后對(duì)數(shù)據(jù)庫(kù)進(jìn)行增绒怨、刪纯赎、改、查操作南蹂,最后關(guān)閉數(shù)據(jù)庫(kù)犬金。
數(shù)據(jù)庫(kù)創(chuàng)建
創(chuàng)建FMDatabase對(duì)象時(shí)參數(shù)為SQLite數(shù)據(jù)庫(kù)文件路徑,該路徑有三種情況六剥。
- 具體文件路徑:如果不存在會(huì)自動(dòng)創(chuàng)建
- 空字符串@"":會(huì)在臨時(shí)目錄創(chuàng)建一個(gè)空的數(shù)據(jù)庫(kù)晚顷,當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)庫(kù)文件也被刪除
- nil:會(huì)創(chuàng)建一個(gè)內(nèi)存中臨時(shí)數(shù)據(jù)庫(kù)疗疟,當(dāng)FMDatabase連接關(guān)閉時(shí)该默,數(shù)據(jù)庫(kù)會(huì)被銷毀
代碼示例:
1、使用FMDataBase類建立數(shù)據(jù)庫(kù):
// 打開(kāi)數(shù)據(jù)庫(kù)-創(chuàng)建表
- (void)createEditableCopyOfDatabaseIfNeeded {
// 獲得數(shù)據(jù)庫(kù)文件的路徑
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) lastObject];
NSString *fileName = [doc stringByAppendingPathComponent:@"student.sqlite"];
// 獲得數(shù)據(jù)庫(kù)
FMDatabase *db = [FMDatabase databaseWithPath:fileName];
// 使用如下語(yǔ)句策彤,如果打開(kāi)失敗栓袖,可能是權(quán)限不足或者資源不足。通常打開(kāi)完操作操作后店诗,需要調(diào)用 close 方法來(lái)關(guān)閉數(shù)據(jù)庫(kù)裹刮。在和數(shù)據(jù)庫(kù)交互之前,數(shù)據(jù)庫(kù)必須是打開(kāi)的必搞。如果資源或權(quán)限不足無(wú)法打開(kāi)或創(chuàng)建數(shù)據(jù)庫(kù)必指,都會(huì)導(dǎo)致打開(kāi)失敗。
if ([db open]) {// 數(shù)據(jù)庫(kù)打開(kāi)成功
// 創(chuàng)建表
BOOL result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_student (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
if (result) {
NSLog(@"創(chuàng)建表成功");
} else {
NSLog(@"創(chuàng)建表失敗");
}
}
self.db = db;
}
// 插入數(shù)據(jù)
- (void)create {
for (int i = 0; i < 10; i++) {
NSString *name = [NSString stringWithFormat:@"miao-%d", arc4random_uniform(100)];
// 不確定的參數(shù)用?來(lái)占位(后面參數(shù)必須是oc對(duì)象恕洲,塔橡;代表語(yǔ)句結(jié)束)
[self.db executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?, ?);", name, @(arc4random_uniform(40))];
// 參數(shù)是數(shù)組的使用方式
// [self.db executeUpdate:@"INSERT INTO t_student (name, age) VALUES (?, ?);" withArgumentsInArray:@[name, @(arc4random_uniform(40))]];
// 不確定的參數(shù)用%@梅割、%d等來(lái)占位(參數(shù)為原始數(shù)據(jù)類型,執(zhí)行語(yǔ)句不區(qū)分大小寫(xiě))
// [self.db executeUpdateWithFormat:@"INSERT INTO t_student (name, age) VALUES (%@, %d);", name, arc4random_uniform(40)];
}
}
// 刪除數(shù)據(jù)
- (void)remove {
// 刪除表數(shù)據(jù)
// [self.db executeUpdate:@"DELETE FROM t_student;"];
// 刪除整個(gè)表
[self.db executeUpdate:@"DROP TABLE IF EXISTS t_student;"];
// 按照主鍵刪除
// 不確定的參數(shù)用葛家?來(lái)占位 (后面參數(shù)必須是oc對(duì)象,需要將int包裝成OC對(duì)象)
// [self.db executeUpdate:@"delete from t_student where id = ?;",@1];
// 不確定的參數(shù)用%@户辞,%d等來(lái)占位
// [self.db executeUpdateWithFormat:@"delete from t_student where name = %@;",@"李響"];
}
// 修改
- (void)modify {
// [self.db executeUpdate:@"update t_student set name = ? where name = ?",newName,oldName];
}
// 查詢
- (void)findAll
{
// 查詢整個(gè)表
FMResultSet *resultSet = [self.db executeQuery:@"SELECT * FROM t_student"];
// 根據(jù)條件查詢
// FMResultSet *resultSet = [self.db executeQuery:@"select * from t_student where id=?",@1];
// 遍歷結(jié)果
while ([resultSet next]) {
int ID = [resultSet intForColumn:@"id"];
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
NSLog(@"%d %@ %d", ID, name, age);
}
}
// 數(shù)據(jù)庫(kù)銷毀命令SQLdrop ...
- (void)destroy {
// 如果表格存在 則銷毀
[self.db executeUpdate:@"drop table if exists t_student;"];
}
2、使用FMDatabaseQueue類實(shí)現(xiàn)多線程操作:
首先說(shuō)一下事物與非事物癞谒,事物是一個(gè)并發(fā)控制的基本單元底燎,所謂的事務(wù),它是一個(gè)操作序列弹砚,這些操作要么都執(zhí)行双仍,要么都不執(zhí)行,它是一個(gè)不可分割的工作單位桌吃。
事物與非事物朱沃,簡(jiǎn)單的舉例來(lái)說(shuō)就是,事物就是把所有的東西打包在一起茅诱,一次性處理它逗物。而非事務(wù)就是一條一條的來(lái)執(zhí)行并且處理。
代碼示例:
// 打開(kāi)數(shù)據(jù)庫(kù)-創(chuàng)建表
- (void)establish {
// 獲得數(shù)據(jù)庫(kù)文件的路徑
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) lastObject];
NSString *dbPath = [doc stringByAppendingPathComponent:@"test.sqlite"];
NSLog(@"路徑:%@", dbPath);
// 創(chuàng)建一個(gè)隊(duì)列
self.dataBaseQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
[self.dataBaseQueue inDatabase:^(FMDatabase *db) {
if ([db open]) {// 數(shù)據(jù)庫(kù)打開(kāi)成功
// 判斷指定表是否存在
if (![self checkTableExist:@"testTable" withFMData:db]) {
// 創(chuàng)建表
BOOL result = [db executeUpdate:@"create table if not exists testTable (id integer PRIMARY KEY AUTOINCREMENT, name text)"];
if (result) {
NSLog(@"創(chuàng)建表成功");
} else {
NSLog(@"創(chuàng)建表失敗");
}
}
}
else
{
// 關(guān)閉數(shù)據(jù)庫(kù)
[db close];
}
}];
}
// 判斷指定表是否存在
- (BOOL)checkTableExist:(NSString *)tableName withFMData:(FMDatabase *)dbDatabase {
BOOL result;
tableName = [tableName lowercaseString];
FMResultSet *rs = [dbDatabase executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName];
result = [rs next];
[rs close];
return result;
}
插入數(shù)據(jù):
- (void)testResult {
// 非事務(wù)插入數(shù)據(jù)
NSDate *one = [NSDate date];
[self.dataBaseQueue inDatabase:^(FMDatabase *db) {
for (int i = 0; i < 500; i++) {
BOOL iserror = [db executeUpdate:@"insert into testTable (name) values(?)",[NSString stringWithFormat:@"name-%d",i]];
if (!iserror) {
NSLog(@"插入失敗");
}
}
}];
NSDate *two = [NSDate date];
NSTimeInterval first = [two timeIntervalSinceDate:one];
NSLog(@"first = %lf",first);
// 開(kāi)啟事務(wù)插入
NSDate *three = [NSDate date];
[self.dataBaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
BOOL result = YES;
for (int i = 500; i < 1000; i++) {
result = [db executeUpdate:@"insert into testTable (name) values(?)",[NSString stringWithFormat:@"name-%d",i]];
if (!result) {
NSLog(@"插入失敗");
*rollback = YES;
break;
}
}
}];
NSDate *four = [NSDate date];
NSTimeInterval second = [four timeIntervalSinceDate:three];
NSLog(@"second = %lf",second);
// 手動(dòng)事物插入
NSDate *five = [NSDate date];
[self.dataBaseQueue inDatabase:^(FMDatabase *db) {
/**
* 操作放入事物中(加入事物操作)
*/
[db beginTransaction];
for (int i = 1000; i < 1500; i++) {
BOOL iserror = [db executeUpdate:@"insert into testTable (name) values(?)",[NSString stringWithFormat:@"name-%d",i]];
if (!iserror) {
NSLog(@"插入失敗");
}
}
/**
* 提交事物
*/
[db commit];
}];
NSDate *Six = [NSDate date];
NSTimeInterval third = [Six timeIntervalSinceDate:five];
NSLog(@"third = %lf",third);
}
輸出打由蟆:
2017-05-31 00:42:47.773 FMDB應(yīng)用[50737:15432041] 創(chuàng)建表成功
2017-05-31 00:42:48.079 FMDB應(yīng)用[50737:15432041] first = 0.306702
2017-05-31 00:42:48.084 FMDB應(yīng)用[50737:15432041] second = 0.004107
2017-05-31 00:42:48.089 FMDB應(yīng)用[50737:15432041] third = 0.004684
刪除數(shù)據(jù):
// 刪除數(shù)據(jù)
- (void)deleteData {
//[self.dataBaseQueue inDatabase:^(FMDatabase *db) {
// [db executeUpdate:@"DELETE FROM testTable WHERE id=?",@1];
//}];
NSDate *five = [NSDate date];
[self.dataBaseQueue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"delete from testTable where id < 500"];
}];
NSDate *six = [NSDate date];
NSTimeInterval third = [six timeIntervalSinceDate:five];
NSLog(@"third = %lf",third);
NSDate *seven = [NSDate date];
[self.dataBaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"delete from testTable where id >= 500"];
}];
NSDate *eight = [NSDate date];
NSTimeInterval fourth = [eight timeIntervalSinceDate:seven];
NSLog(@"fourth = %lf",fourth);
}
輸出打郁嶙俊:
2017-05-31 00:51:56.617 FMDB應(yīng)用[50890:15440944] third = 0.001500
2017-05-31 00:51:56.619 FMDB應(yīng)用[50890:15440944] fourth = 0.001703
更新數(shù)據(jù):
// 更新
- (void)update {
//[self.dataBaseQueue inDatabase:^(FMDatabase *db) {
// [db executeUpdate:@"UPDATE testTable SET name='lx' WHERE id=?",@8];
//}];
// 如果要保證多個(gè)操作同時(shí)成功或者同時(shí)失敗,用事務(wù)摆寄,即把多個(gè)操作放在同一個(gè)事務(wù)中失暴。
[self.dataBaseQueue inDatabase:^(FMDatabase *db) {
// 開(kāi)啟事務(wù)
[db beginTransaction];
BOOL flag = [db executeUpdate:@"UPDATE testTable SET name='xiugai' WHERE id=?",@2];
[db executeUpdate:@"UPDATE testTable SET name='zheli' WHERE id=?",@3];
if (flag) {
NSLog(@"數(shù)據(jù)更新成功");
} else {
NSLog(@"數(shù)據(jù)更新失敗");
// 事務(wù)回滾
// 發(fā)現(xiàn)情況不對(duì)時(shí),主動(dòng)回滾用下面語(yǔ)句椭迎。否則是根據(jù)commit結(jié)果锐帜,如成功就成功,如不成功才回滾
[db rollback];
[db executeUpdate:@"UPDATE testTable SET name='Eric' WHERE id=?",@4];
}
// 提交事務(wù)
[db commit];
}];
__block BOOL flag;
// FMDB中畜号,也可以直接利用隊(duì)列進(jìn)行事務(wù)操作缴阎,隊(duì)列中的打開(kāi)、關(guān)閉简软、回滾事務(wù)等都已經(jīng)被封裝好了蛮拔。
[self.dataBaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
flag = [db executeUpdate:@"UPDATE testTable SET name='xiaoqiang' WHERE id=?",@2];
[db executeUpdate:@"UPDATE testTable SET name='xiaoming' WHERE id=?",@3];
if (flag) {
NSLog(@"數(shù)據(jù)更新成功");
*rollback = !flag;
} else {
NSLog(@"數(shù)據(jù)更新失敗");
// 發(fā)現(xiàn)情況不對(duì)時(shí),主動(dòng)回滾用下面語(yǔ)句痹升。
*rollback = YES;
flag = NO;
[db executeUpdate:@"UPDATE testTable SET name='Eric' WHERE id=?",@4];
}
}];
}
// 其他線程操作
- (void)multithreading {
// 異步并發(fā)全局隊(duì)列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.dataBaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
BOOL result = YES;
for (int i = 500; i < 1000; i++) {
result = [db executeUpdate:@"insert into testTable (name) values(?)",[NSString stringWithFormat:@"name-%d",i]];
if (!result) {
NSLog(@"break");
*rollback = YES;
break;
}
}
}];
});
}
10建炫、Magical Record
Magical Record借用了Ruby on Rails中的Active Record模式,使得你可以非常容易的添加疼蛾、查找肛跌、刪除數(shù)據(jù),降低了Core Data的使用門檻。
創(chuàng)建模型文件:
創(chuàng)建一個(gè)名為Model的模型文件衍慎。 (File > New File… > Core Data > Data Model)
點(diǎn)擊左下角的Add Entity转唉,更改Entity的名字為Person。
為Entity添加三個(gè)Attribute:age(Integer16)稳捆、firstname(string)赠法、lastname(string)。
點(diǎn)擊Editor > Create NSManagedObject Subclass… 創(chuàng)建實(shí)體分類乔夯。
使用Magical Record:
引入頭文件
#import "MagicalRecord.h" 或 #import <MagicalRecord/MagicalRecord.h>
如果在創(chuàng)建工程之初勾選了使用Core Data的選項(xiàng)砖织,系統(tǒng)會(huì)自動(dòng)在AppDelegate中生成大量的Core Data代碼。但是完可以使用一行代碼代替末荐,如下:
初始化
標(biāo)準(zhǔn)初始化
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[MagicalRecord setupCoreDataStackWithStoreNamed:@"MyDatabase.sqlite"];
// 或者
/*
+ (void)setupCoreDataStack;
+ (void)setupAutoMigratingCoreDataStack;
+ (void)setupCoreDataStackWithInMemoryStore;
+ (void)setupCoreDataStackWithStoreNamed:(NSString *)storeName;
+ (void)setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName;
+ (void)setupCoreDataStackWithStoreAtURL:(NSURL *)storeURL;
+ (void)setupCoreDataStackWithAutoMigratingSqliteStoreAtURL:(NSURL *)storeURL;
*/
return YES;
}
每次調(diào)用都會(huì)實(shí)例化一塊 CoreData 棧侧纯,并且對(duì)該實(shí)例提供 getter 和 setter 方法。 MagicalRecord 將這些實(shí)例作為默認(rèn)使用的棧鞠评。
當(dāng)在DeBug模式下使用默認(rèn)SQLite 數(shù)據(jù)存儲(chǔ)時(shí)茂蚓,如果沒(méi)有創(chuàng)建新數(shù)據(jù)模型就更改了數(shù)據(jù)模型,MagicalRecord 將會(huì)自動(dòng)刪除舊的存儲(chǔ)并創(chuàng)建新的存儲(chǔ)剃幌。這將為你節(jié)省大量時(shí)間——每當(dāng)更改數(shù)據(jù)模型,不必再卸載、重裝應(yīng)用晾浴。但請(qǐng)確保發(fā)布的應(yīng)用的不是DeBug版本:否則在未告知用戶的情況下刪除應(yīng)用數(shù)據(jù)负乡,這可不是好事!
退出前,你應(yīng)該調(diào)用類方法+cleanUp:
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
[MagicalRecord cleanUp];
}
這用于使用MagicalRecord后的整理工作:解除我們自定義的錯(cuò)誤處理器并把MagicalRecord創(chuàng)建的所有的Core Data 棧設(shè)為 nil脊凰。
開(kāi)啟iCloud 持久化存儲(chǔ)
為了更好地使用蘋(píng)果的iCloud Core Data 同步機(jī)制抖棘,使用下面初始化方法中的 一種 來(lái)替換來(lái)替換前面列出的標(biāo)準(zhǔn)初始化方法:
+ (void)setupCoreDataStackWithiCloudContainer:(NSString *)containerID
localStoreNamed:(NSString *)localStore;
+ (void)setupCoreDataStackWithiCloudContainer:(NSString *)containerID
contentNameKey:(NSString *)contentNameKey
localStoreNamed:(NSString *)localStoreName
cloudStorePathComponent:(NSString *)pathSubcomponent;
+ (void)setupCoreDataStackWithiCloudContainer:(NSString *)containerID
contentNameKey:(NSString *)contentNameKey
localStoreNamed:(NSString *)localStoreName
cloudStorePathComponent:(NSString *)pathSubcomponent
completion:(void (^)(void))completion;
+ (void)setupCoreDataStackWithiCloudContainer:(NSString *)containerID
localStoreAtURL:(NSURL *)storeURL;
+ (void)setupCoreDataStackWithiCloudContainer:(NSString *)containerID
contentNameKey:(NSString *)contentNameKey
localStoreAtURL:(NSURL *)storeURL
cloudStorePathComponent:(NSString *)pathSubcomponent;
+ (void)setupCoreDataStackWithiCloudContainer:(NSString *)containerID
contentNameKey:(NSString *)contentNameKey
localStoreAtURL:(NSURL *)storeURL
cloudStorePathComponent:(NSString *)pathSubcomponent
completion:(void (^)(void))completion;
如果你需要同時(shí)管理多個(gè)iCloud 存儲(chǔ)內(nèi)容,推薦使用可以設(shè)置 contentNameKey 的方法狸涌。不能設(shè)置contentNameKey的方法則會(huì)根據(jù)應(yīng)用的 bundle identifier(CFBundleIdentifier)自動(dòng)生成 NSPersistentStoreUbiquitousContentNameKey作為標(biāo)示切省。
操作被管理的對(duì)象上下文
對(duì)象上下文環(huán)境是你操作Core Data內(nèi)數(shù)據(jù)的基礎(chǔ),只有正確獲取到了上下文環(huán)境帕胆,才有可能進(jìn)行相關(guān)的讀寫(xiě)操作朝捆。換句話說(shuō),程序的任意位置,只要能正確獲取上下文懒豹,都能進(jìn)行Core Data的操作.這也是使用Core Data共享數(shù)據(jù)的基礎(chǔ)之一芙盘。相較于傳統(tǒng)的方式,各個(gè)頁(yè)面之間只需要與一個(gè)透明的上下文環(huán)境進(jìn)行交互脸秽,即可進(jìn)行頁(yè)面間數(shù)據(jù)的共享儒老。
創(chuàng)建新的對(duì)象上下文:
+ [NSManagedObjectContext MR_context]: 設(shè)置默認(rèn)的上下文為它的父級(jí)上下文.并發(fā)類型為 NSPrivateQueueConcurrencyType .
+ [NSManagedObjectContext MR_newMainQueueContext]: 并發(fā)類型為 ** NSMainQueueConcurrencyType**.
+ [NSManagedObjectContext MR_newPrivateQueueContext]: 并發(fā)類型為 NSPrivateQueueConcurrencyType .
+ [NSManagedObjectContext MR_contextWithParent:…]: 允許自定義父級(jí)上下文.并發(fā)類型為 NSPrivateQueueConcurrencyType .
+ [NSManagedObjectContext MR_contextWithStoreCoordinator:…]:允許自定義持久化存儲(chǔ)協(xié)調(diào)器.并發(fā)類型為 NSPrivateQueueConcurrencyType .
例:
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_context];
// 在當(dāng)前上下文環(huán)境中創(chuàng)建一個(gè)新的 Person 對(duì)象.
Person *person = [Person MR_createEntityInContext:localContext];
person.firstname = @"默";
person.lastname = @"六";
person.age = 24;
// 保存修改到當(dāng)前上下文中.
[localContext MR_saveToPersistentStoreAndWait];
默認(rèn)上下文:
當(dāng)使用Core Data時(shí),你經(jīng)常使用的兩個(gè)類主要對(duì)象是:NSManagedObject和NSManagedObjectContext记餐。
MagicalRecord 提供了一個(gè)簡(jiǎn)單類方法來(lái)獲取一個(gè)默認(rèn)的NSManagedObjectContext對(duì)象驮樊,這個(gè)對(duì)象在整個(gè)應(yīng)用全局可用。這個(gè)上下文對(duì)象,在主線程操作囚衔,對(duì)于簡(jiǎn)單的單線程應(yīng)用來(lái)說(shuō)非常強(qiáng)大挖腰。
獲取方法:
NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext];
這個(gè)上下文對(duì)象,在MagicalRecord的任何需要使用上下文對(duì)象方法中都可以使用佳魔,但是并不需要給這些方法顯示提供一個(gè)指定對(duì)象管理上下文對(duì)象參數(shù)曙聂。
如果你想創(chuàng)建一個(gè)新的對(duì)象管理上下文對(duì)象,以用于非主線程鞠鲜,可使用下面的方法:
NSManagedObjectContext *myNewContext = [NSManagedObjectContext MR_context];
這將會(huì)創(chuàng)建一個(gè)新的對(duì)象管理上下文,和默認(rèn)的上下文對(duì)象有相同的對(duì)象模型和持久化存儲(chǔ);但是在另一個(gè)線程中使用時(shí)宁脊,是線程安全的,它自動(dòng)設(shè)置默認(rèn)上下文對(duì)象為父級(jí)上下文贤姆。
如果你想要將你的myNewContext實(shí)例作為所有獲取請(qǐng)求默認(rèn)的上下文對(duì)象,使用下面的類方法:
[NSManagedObjectContext MR_setDefaultContext:myNewContext];
[注意]: 建議默認(rèn)的上下文對(duì)象在主線程使用并發(fā)類型為NSMainQueueConcurrencyType的對(duì)象管理上線文對(duì)象創(chuàng)建和設(shè)置榆苞。
在后臺(tái)線程中執(zhí)行任務(wù)
MagicalRecord 提供了創(chuàng)建及使用后臺(tái)線程上下文的方法,按順序的在后臺(tái)保存霞捡。后臺(tái)保存操作為代碼塊的形式坐漏。
- 用于更改實(shí)體的block將永遠(yuǎn)不會(huì)在主線程執(zhí)行。
- 在你的block內(nèi)部提供一個(gè)單一的 NSManagedObjectContext 上下文對(duì)象碧信。
例如赊琳,如果我們有一個(gè)Person實(shí)體對(duì)象,并且我們需要設(shè)置它的firstName和lastName字段砰碴,下面的代碼展示了如何使用MagicalRecord來(lái)設(shè)置一個(gè)后臺(tái)保存的上下文對(duì)象:
// 獲取上下文環(huán)境
NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext];
// 在當(dāng)前上下文環(huán)境中創(chuàng)建一個(gè)新的 Person 對(duì)象.
Person *person = [Person MR_createEntityInContext:defaultContext];
person.firstname = @"firstname";
person.lastname = @"lastname";
person.age = 100;
// 保存修改到當(dāng)前上下文中.
[defaultContext MR_saveToPersistentStoreAndWait];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
Person *localPerson = [person MR_inContext:localContext];
localPerson.firstname = @"Yan";
localPerson.lastname = @"Feng";
}];
在這個(gè)方法中靠欢,指定的block給你提供了一個(gè)合適的上下文對(duì)象來(lái)執(zhí)行你的操作犬绒,你不需要擔(dān)心這個(gè)上下文對(duì)象的初始化來(lái)告訴默認(rèn)上線文它準(zhǔn)備好了适袜,并且應(yīng)當(dāng)更新,因?yàn)樽兏窃诹硪粋€(gè)線程執(zhí)行堪侯。
為了在保存block完成時(shí)執(zhí)行某個(gè)操作,你可以使用 completion block:
// 獲取上下文環(huán)境
NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext];
// 在當(dāng)前上下文環(huán)境中創(chuàng)建一個(gè)新的 Person 對(duì)象猖辫。
Person *person = [Person MR_createEntityInContext:defaultContext];
person.firstname = @"firstname";
person.lastname = @"lastname";
person.age = 100;
// 保存修改到當(dāng)前上下文中酥泞。
[defaultContext MR_saveToPersistentStoreAndWait];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
Person *localPerson = [person MR_inContext:localContext];
localPerson.firstname = @"Yan";
localPerson.lastname = @"Feng";
} completion:^(BOOL success, NSError *error) {
NSArray * persons = [Person MR_findAll];
[persons enumerateObjectsUsingBlock:^(Person * obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"firstname: %@, lastname: %@\n", obj.firstname, obj.lastname);
}];
}];
// 這個(gè)完成的block,在主線程(隊(duì)列)中調(diào)用啃憎,所以可以在此block里安全觸發(fā)UI更新芝囤。
創(chuàng)建實(shí)體對(duì)象
為了創(chuàng)建并插入一個(gè)新的實(shí)體實(shí)例到默認(rèn)上下文對(duì)象中,你可以使用:
Person *myPerson = [Person MR_createEntity];
創(chuàng)建實(shí)體實(shí)例荧飞,并插入到指定的上下文中:
Person *myPerson = [Person MR_createEntityInContext:otherContext];
刪除實(shí)體對(duì)象
刪除默認(rèn)上下文中的實(shí)體對(duì)象:
[myPerson MR_deleteEntity];
刪除指定上下文中的實(shí)體對(duì)象:
[myPerson MR_deleteEntityInContext:otherContext];
刪除默認(rèn)上下文中的所有實(shí)體:
[Person MR_truncateAll];
刪除指定上下文中的所有實(shí)體:
[Person MR_truncateAllInContext:otherContext];
獲取實(shí)體對(duì)象
基礎(chǔ)查找
MagicalRecord中的大多數(shù)方法返回NSArray結(jié)果.
舉個(gè)例子凡人,如果你有一個(gè)名為 Person 的實(shí)體,和實(shí)體 Department 關(guān)聯(lián),你可以從持久化存儲(chǔ)中獲取所有的 Person 實(shí)體:
NSArray *people = [Person MR_findAll];
可以指定以某個(gè)屬性排序:
NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName" ascending:YES];
可以使用多個(gè)屬性進(jìn)行排序:
NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName,FirstName" ascending:YES];
當(dāng)使用多個(gè)屬性進(jìn)行排序時(shí)叹阔,可以單獨(dú)指定升序或降序.
// 按照屬性LastName降序挠轴、FirstName升序排序
NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName:NO,FirstName" ascending:YES];
// 或者 NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName,FirstName:YES" ascending:NO];
如果你有辦法通過(guò)某種方式從數(shù)據(jù)庫(kù)中獲取唯一的一個(gè)對(duì)象(比如,給對(duì)象一個(gè)特定的唯一標(biāo)記),你可以使用下面方法獲取某個(gè)實(shí)體對(duì)象:
Person *person = [Person MR_findFirstByAttribute:@"FirstName" withValue:@"名字"];
高級(jí)查找
使用 謂詞 查詢特定實(shí)體:
NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", @[dept1, dept2]];
NSArray *people = [Person MR_findAllWithPredicate:peopleFilter];
使用 謂詞 查詢特定實(shí)體:
使用謂詞獲取對(duì)應(yīng)查詢條件的 NSFetchRequest 對(duì)象
NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
NSFetchRequest *people = [Person MR_requestAllWithPredicate:peopleFilter];
每執(zhí)行一次,就創(chuàng)建一個(gè)這些查詢條件對(duì)應(yīng)的NSFetchRequest和NSSortDescriptor.
自定義查詢請(qǐng)求
NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments];
NSFetchRequest *peopleRequest = [Person MR_requestAllWithPredicate:peopleFilter];
[peopleRequest setReturnsDistinctResults:NO];
[peopleRequest setReturnPropertiesNamed:@[@"FirstName", @"LastName"]];
NSArray *people = [Person MR_executeFetchRequest:peopleRequest];
查詢實(shí)體數(shù)目
你還可以獲取指定類型的實(shí)體個(gè)數(shù):
NSNumber *count = [Person MR_numberOfEntities];
使用謂詞或其它過(guò)濾器查詢實(shí)體數(shù)目:
NSNumber *count = [Person MR_numberOfEntitiesWithPredicate:...];
以下方法返回類型則是NSUInteger:
+ (NSUInteger) MR_countOfEntities;
+ (NSUInteger) MR_countOfEntitiesWithContext:(NSManagedObjectContext *)context;
+ (NSUInteger) MR_countOfEntitiesWithPredicate:(NSPredicate *)searchFilter;
+ (NSUInteger) MR_countOfEntitiesWithPredicate:(NSPredicate *)searchFilter
inContext:(NSManagedObjectContext *)context;
合計(jì)操作
NSNumber *totalCalories = [CTFoodDiaryEntry MR_aggregateOperation:@"sum:" onAttribute:@"calories" withPredicate:predicate];
NSNumber *mostCalories = [CTFoodDiaryEntry MR_aggregateOperation:@"max:" onAttribute:@"calories" withPredicate:predicate];
NSArray *caloriesByMonth = [CTFoodDiaryEntry MR_aggregateOperation:@"sum:" onAttribute:@"calories" withPredicate:predicate
groupBy:@"month"];
在指定上下文中查找實(shí)體
所有的 find,fetch耳幢,request 方法都可以通過(guò) inContext: 參數(shù)指定查詢的上下文:
NSArray *peopleFromAnotherContext = [Person MR_findAllInContext:someOtherContext];
Person *personFromContext = [Person MR_findFirstByAttribute:@"lastName" withValue:@"名字" inContext:someOtherContext];
NSUInteger count = [Person MR_numberOfEntitiesWithContext:someOtherContext];
保存實(shí)體
大多數(shù)情況下岸晦,當(dāng)數(shù)據(jù)發(fā)生變化的時(shí)候就執(zhí)行保存操作欧啤。有一些應(yīng)用只是在應(yīng)用關(guān)閉的時(shí)候才保存,但這樣增加了丟失數(shù)據(jù)的風(fēng)險(xiǎn)启上。當(dāng)應(yīng)用崩潰的時(shí)候就會(huì)造成數(shù)據(jù)丟失邢隧,用戶會(huì)丟失他們的數(shù)據(jù)。應(yīng)當(dāng)避免這種情況的發(fā)生冈在。
如果保存操作花費(fèi)的時(shí)間太長(zhǎng)倒慧,你可以采取以下措施:
1.在后臺(tái)線程中執(zhí)行保存操作:
MagicalRecord 為更改、保存實(shí)體提供了簡(jiǎn)潔的API包券。
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
// Do your work to be saved here, against the `localContext` instance
// 在此塊中的所有操作均在后臺(tái)執(zhí)行
} completion:^(BOOL success, NSError *error) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
將任務(wù)拆分成小任務(wù)保存:
像一次性導(dǎo)入大量數(shù)據(jù)的任務(wù)纫谅,你需要將數(shù)據(jù)拆分成小塊來(lái)操作。一次處理的數(shù)據(jù)大小并沒(méi)有統(tǒng)一標(biāo)準(zhǔn)溅固,你需要使用類似 Apples’ Instruments 的工具來(lái)測(cè)試調(diào)整付秕,獲取最佳大小。
處理長(zhǎng)時(shí)儲(chǔ)存
ios平臺(tái)
當(dāng)iOS平臺(tái)app退出時(shí)侍郭,app會(huì)獲得短暫時(shí)間向硬盤中保存整理數(shù)據(jù)询吴。如果你的應(yīng)用存儲(chǔ)時(shí)間較長(zhǎng),最好在應(yīng)用完全終止前亮元,請(qǐng)求延長(zhǎng)后臺(tái)執(zhí)行時(shí)間猛计。
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
// 此處執(zhí)行保存操作
} completion:^(BOOL success, NSError *error) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
請(qǐng)確保認(rèn)真閱讀beginBackgroundTaskWithExpirationHandler ,因?yàn)椴贿m當(dāng)或不必要的延長(zhǎng)應(yīng)用生命周期爆捞,申請(qǐng)上架的時(shí)候可能會(huì)被 App Store 拒絕有滑。
OS X 平臺(tái)
OS X Mavericks (10.9) 及更高版本中,App Nap 可以讓你 app 看起來(lái)像關(guān)閉了嵌削,但仍在后臺(tái)運(yùn)行。如果有長(zhǎng)時(shí)間的保存操作望艺,最好的方式是禁用自動(dòng)終止和突然終止( automatic and sudden termination):
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
[processInfo disableSuddenTermination];
[processInfo disableAutomaticTermination:@"Application is currently saving to persistent store"];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
// Do your work to be saved here
} completion:^(BOOL success, NSError *error) {
[processInfo enableSuddenTermination];
[processInfo enableAutomaticTermination:@"Application has finished saving to the persistent store"];
}];
導(dǎo)入對(duì)象
導(dǎo)入之前你應(yīng)當(dāng)充分了解導(dǎo)入數(shù)據(jù)對(duì)象的結(jié)構(gòu)苛秕,同時(shí)構(gòu)建與之對(duì)應(yīng)的實(shí)體。完成這些之后找默,再進(jìn)行數(shù)據(jù)導(dǎo)入艇劫。導(dǎo)入的方式有以下幾種:
依據(jù)數(shù)據(jù)對(duì)象自動(dòng)創(chuàng)建新實(shí)例:
NSDictionary *contactInfo = // 此處解析 JSON 或其他數(shù)據(jù)源
Person *importedPerson = [Person MR_importFromObject:contactInfo];
兩步方法:
NSDictionary *contactInfo = // 此處解析 JSON 或其他數(shù)據(jù)源
Person *person = [Person MR_createEntity]; // 此處不一定是新建實(shí)體,也可使用已有實(shí)體
[person MR_importValuesForKeysWithObject:contactInfo];
兩步方法中的實(shí)體可以是新建的惩激,也可以是已存在的店煞,兩步方法可以幫你更新已存在的實(shí)體。
+MR_importFromObject:根據(jù)已配置的查詢值(lookup value)查找相應(yīng)對(duì)象(即上文中的relatedByAttribute 和 attributeNameID)风钻。它遵循Cocoa導(dǎo)入key-value 的范例顷蟀,保證安全導(dǎo)入數(shù)據(jù)。
+MR_importFromObject: 是對(duì)實(shí)例方法-MR_importValuesForKeysWithObject:和創(chuàng)建對(duì)象方法的封裝骡技,返回值為賦值的新對(duì)象鸣个。
以上兩個(gè)方法均為同步執(zhí)行方法羞反。導(dǎo)入時(shí)間長(zhǎng)短不一,很可能影響交互體驗(yàn)囤萤,若將所有數(shù)據(jù)導(dǎo)入工作都放在后臺(tái)執(zhí)行昼窗,就不會(huì)影響軟件交互了。像前面提到的涛舍,MagicalRecord 提供了很多好用的后臺(tái)線程 API :
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *)localContext {
Person *importedPerson = [Person MR_importFromObject:personRecord inContext:localContext]; //導(dǎo)入操作
}];
導(dǎo)入數(shù)組
使用 array 對(duì)象來(lái)保存單一類型數(shù)據(jù)很常見(jiàn)澄惊,可以使用 +MR_importFromArray: 方法來(lái)處理:
NSArray *arrayOfPeopleData = /// 解析JSON的結(jié)果
NSArray *people = [Person MR_importFromArray:arrayOfPeopleData];
此方法與+MR_importFromObject:方法類似,也是同步執(zhí)行方法富雅,所以你想要后臺(tái)執(zhí)行掸驱,就得使用前面提到的方法,在后臺(tái)塊里執(zhí)行吹榴。
如果你導(dǎo)入的數(shù)據(jù)與Core Data model 完全匹配亭敢,那么上面的方法已經(jīng)能幫你完成導(dǎo)入工作了。但大多數(shù)情況下并不理想图筹,兩者之間稍有差別, MagicalRecord 的一些方法處理導(dǎo)入數(shù)據(jù)和Core Data model之間的不同帅刀。
實(shí)踐:
錯(cuò)誤數(shù)據(jù)處理
API 方法經(jīng)常會(huì)返回格式錯(cuò)誤或內(nèi)容錯(cuò)誤的數(shù)據(jù)。最好的處理方法是通過(guò)實(shí)體類的分類方法處理远剩,有三個(gè)方法可供選擇:
Method | Purpose |
---|---|
- (BOOL) shouldImport; | 在導(dǎo)入數(shù)據(jù)前調(diào)用此方法扣溺。如果返回為 NO,則取消向?qū)嶓w導(dǎo)入數(shù)據(jù)瓜晤。 |
- (void) willImport:(id)data; | 緊鄰數(shù)據(jù)導(dǎo)入之前調(diào)用锥余。 |
- (void) didImport:(id)data; | 緊鄰數(shù)據(jù)導(dǎo)入之后調(diào)用。 |
一般來(lái)說(shuō)痢掠,如果你嘗試導(dǎo)入所有數(shù)據(jù)時(shí)驱犹,發(fā)現(xiàn)一些損壞數(shù)據(jù),你可能想修復(fù)它們足画。
常見(jiàn)的情景是在導(dǎo)入JSON數(shù)據(jù)時(shí)雄驹,常常將數(shù)字字符串被錯(cuò)誤的解析為數(shù)字。如果你想確保它們以字符串的類型導(dǎo)入淹辞,可以這么做:
@interface MyGreatEntity
@property(readwrite, nonatomic, copy) NSString *identifier;
@end
@implementation MyGreatEntity
@dynamic identifier;
- (void)didImport:(id)data
{
if (NO == [data isKindOfClass:[NSDictionary class]]) {
return;
}
NSDictionary *dataDictionary = (NSDictionary *)data;
id identifierValue = dataDictionary[@"my_identifier"];
if ([identifierValue isKindOfClass:[NSNumber class]]) {
NSNumber *numberValue = (NSNumber *)identifierValue;
self.identifier = [numberValue stringValue];
}
}
@end
更新數(shù)據(jù)時(shí)刪除本地記錄
在后續(xù)的導(dǎo)入操作中医舆,有時(shí)不僅需要更新記錄,同時(shí)要?jiǎng)h除本地存在象缀、遠(yuǎn)程數(shù)據(jù)庫(kù)中不存在的數(shù)據(jù)蔬将。此時(shí)需要先根據(jù) relatedByAttribute來(lái)判斷哪些記錄不在遠(yuǎn)程數(shù)據(jù)庫(kù)中,然后將其刪除央星。
NSArray *arrayOfPeopleData = /// JSON 解析結(jié)果
NSArray *people = [Person MR_importFromArray:arrayOfPeopleData];
NSArray *idList = [arrayOfPeopleData valueForKey:@"id"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(id IN %@)", idList];
[Person MR_deleteAllMatchingPredicate:predicate];
如果你想在更新操作中確保相關(guān)記錄已被刪除霞怀,你可以使用與上面代碼類似的邏輯,并在Person 的 willImport:方法中實(shí)現(xiàn):
@implementation Person
- (void)willImport:(id)data {
NSArray *idList = [data[@"posts"] valueForKey:@"id"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(id IN %@) AND person.id == %@", idList, self.id];
[Post MR_deleteAllMatchingPredicate:predicate];
}
[注]:如不想使用MR_前綴等曼,只需要在*-Prefix.pch文件中添加一句#define MR_SHORTHAND即可里烦,注意這句要在#import “MagicalRecord.h”之前凿蒜。