沙盒詳解html
一、IOS沙盒機制ios
IOS應用程序只能在爲該改程序建立的文件系統中讀取文件,不能夠去其它地方訪問,此區域被成爲沙盒,因此全部的非代碼文件都要保存在此,例如圖像,圖標,聲音,映像,屬性列表,文本文件等。git
1.一、每一個應用程序都有本身的存儲空間程序員
1.二、應用程序不能翻過本身的圍牆去訪問別的存儲空間的內容github
1.三、應用程序請求的數據都要經過權限檢測,假如不符合條件的話,不會被放行。web
經過這張圖只能從表層上理解sandbox是一種安全體系,應用程序的全部操做都要經過這個體系來執行,其中核心內容是:sandbox對應用程序執行各類操做的權限限制。正則表達式
二、打開模擬器沙盒目錄sql
下面看看模擬器的沙盒文件夾在mac電腦上的什麼位置。數據庫
文件都在我的用戶名文件夾下的一個隱藏文件夾裏,中文叫資源庫,他的目錄實際上是Library。編程
2.1 方法一、能夠設置顯示隱藏文件,而後在Finder下直接打開。設置查看隱藏文件的方法以下:打開終端,輸入命名
顯示Mac隱藏文件的命令:defaults write com.apple.finder AppleShowAllFiles -bool true
隱藏Mac隱藏文件的命令:defaults write com.apple.finder AppleShowAllFiles -bool false
輸完單擊Enter鍵,退出終端,
從新啓動Finder就能夠了重啓Finder:鼠標單擊窗口左上角的蘋果標誌-->強制退出-->Finder-->
如今能看到資源庫文件夾了。
打開資源庫後找到/Application Support/iPhone Simulator/文件夾。這裏面就是模擬器的各個程序的沙盒目錄了。
2.2 方法二、這種方法更方便,在Finder上點->前往->前往文件夾,輸入/Users/username/Library/Application Support/iPhone Simulator/ 前往。
username這裏寫你的用戶名。
三、目錄結構
默認狀況下,每一個沙盒含有3個文件夾:Documents, Library 和 tmp。由於應用的沙盒機制,應用只能在幾個目錄下讀寫文件
Documents:蘋果建議將程序中創建的或在程序中瀏覽到的文件數據保存在該目錄下,iTunes備份和恢復的時候會包括此目錄
Library:存儲程序的默認設置或其它狀態信息;
Library/Caches:存放緩存文件,iTunes不會備份此目錄,此目錄下文件不會在應用退出刪除
tmp:提供一個即時建立臨時文件的地方。
iTunes在與iPhone同步時,備份全部的Documents和Library文件。
iPhone在重啓時,會丟棄全部的tmp文件。
咱們建立一個IosSandbox的項目來展開沙盒和文件讀寫等操做的練習。
建立後找到模擬器上對應的目錄,
這是目錄全展開了。
這是上面提到的三個目錄 :Documents、Library、 tmp
下篇介紹目錄路徑獲取和文件操做
咱們看看如何獲取應用程序沙盒目錄。包括真機的沙盒的目錄。
一、獲取程序的Home目錄
NSString *homeDirectory = NSHomeDirectory(); NSLog(@"path:%@", homeDirectory); |
打印結果:
2012-06-17 14:00:06.098 IosSandbox[3536:f803] /Users/rongfzh/Library/Application Support/iPhone Simulator/5.1/ Applications/3B8EC78A-5EEE-4C2F-B0CB-4C3F02B996D2 |
那在真機上的目錄有是怎麼樣的呢?咱們看看
2012-06-17 14:25:47.059 IosSandbox[4281:f803] /var/mobile/Applications/3B8EC78A-5EEE-4C2F-B0CB-4C3F02B996D2
可見,真機上的目錄是/var/mobile/Applications/這個目錄下的,和模擬器不同。這個是Home目錄,其餘的子目錄和模擬器同樣。
二、獲取document目錄
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *path = [paths objectAtIndex:0]; NSLog(@"path:%@", path); |
打印結果
2012-06-17 14:00:06.099 IosSandbox[3536:f803] path:/Users/rongfzh/Library/Application Support/iPhone Simulator/5.1 /Applications/3B8EC78A-5EEE-4C2F-B0CB-4C3F02B996D2/Documents |
三、獲取Cache目錄
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *path = [paths objectAtIndex:0]; NSLog(@"%@", path); |
打印結果:
2012-06-17 14:03:50.431 IosSandbox[3628:f803] /Users/rongfzh/Library/Application Support/iPhone Simulator/5.1 /Applications/3B8EC78A-5EEE-4C2F-B0CB-4C3F02B996D2/Library/Caches |
四、獲取Library目錄
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); NSString *path = [paths objectAtIndex:0]; NSLog(@"%@", path); |
打印結果:
2012-06-17 14:07:17.544 IosSandbox[3733:f803] /Users/rongfzh/Library/Application Support/iPhone Simulator/5.1 /Applications/3B8EC78A-5EEE-4C2F-B0CB-4C3F02B996D2/Library |
五、獲取Tmp目錄
NSString *tmpDir = NSTemporaryDirectory(); NSLog(@"%@", tmpDir); |
打印結果:
2012-06-17 14:08:07.824 IosSandbox[3782:f803] /var/folders/g7/246bh79130zblw0yjjtc55cw0000gn/T/ |
六、寫入文件
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docDir = [paths objectAtIndex:0]; if (!docDir) { NSLog(@"Documents 目錄未找到"); } NSArray *array = [[NSArray alloc] initWithObjects:@"內容",@"content",nil]; NSString *filePath = [docDir stringByAppendingPathComponent:@"testFile.txt"]; [array writeToFile:filePath atomically:YES]; |
注:咱們在真機上也運行一下,把文件寫入,下一步從真機上把內容讀取出來。
寫入輸入 array ,裏面是兩個字符串,一會咱們讀出來打印。
寫入咱們在程序沙盒目錄下看到文件 testFile.txt
打開文件看到的內容是這樣的,是個xml格式的plist文件,數據格式保存了內容。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <array> <string>內容</string> <string>content</string> </array> </plist> |
七、讀取文件
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docDir = [paths objectAtIndex:0]; NSString *filePath = [docDir stringByAppendingPathComponent:@"testFile.txt"]; NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath]; NSLog(@"%@", array); |
打印結果:
把上面的文件解析後,把內容打印出來了。
2012-06-17 14:14:46.249 IosSandbox[3918:f803] ( "\U5185\U5bb9", content ) |
真機上讀取並打印文件路徑:
2012-06-17 14:25:47.059 IosSandbox[4281:f803] /var/mobile/Applications/3B8EC78A-5EEE-4C2F-B0CB-4C3F02B996D2/Documents/testFile.txt
(
"\U5185\U5bb9",
content
)
真機上也能寫入和打印。
咱們看看NSFileManager如何使用。包括建立文件,目錄,刪除,遍歷目錄等。
一、在Documents裏建立目錄
建立一個叫test的目錄,先找到Documents的目錄,
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSLog(@"documentsDirectory%@",documentsDirectory); NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *testDirectory = [documentsDirectory stringByAppendingPathComponent:@"test"]; // 建立目錄 [fileManager createDirectoryAtPath:testDirectory withIntermediateDirectories:YES attributes:nil error:nil]; |
啓動程序,這時候目錄就建立了:
二、在test目錄下建立文件
建立文件怎麼辦呢?接着上面的代碼 testPath 要用stringByAppendingPathComponent拼接上你要生成的文件名,好比test00.txt。這樣才能在test下寫入文件。
testDirectory是上面代碼生成的路徑哦,不要忘了。我往test文件夾裏寫入三個文件,test00.txt ,test22.txt,text.33.txt。內容都是寫入內容,write String。
實現代碼以下:
NSString *testPath = [testDirectory stringByAppendingPathComponent:@"test00.txt"]; NSString *testPath2 = [testDirectory stringByAppendingPathComponent:@"test22.txt"]; NSString *testPath3 = [testDirectory stringByAppendingPathComponent:@"test33.txt"]; NSString *string = @"寫入內容,write String"; [fileManager createFileAtPath:testPath contents:[string dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; [fileManager createFileAtPath:testPath2 contents:[string dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; [fileManager createFileAtPath:testPath3 contents:[string dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; |
看下面的圖,三個文件都出來了,內容也對。
在Documents目錄下建立就更簡單了,不用加test就ok了
三、獲取目錄列裏全部文件名
兩種方法獲取:subpathsOfDirectoryAtPath 和 subpathsAtPath
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSLog(@"documentsDirectory%@",documentsDirectory); NSFileManager *fileManage = [NSFileManager defaultManager]; NSString *myDirectory = [documentsDirectory stringByAppendingPathComponent:@"test"]; NSArray *file = [fileManage subpathsOfDirectoryAtPath: myDirectory error:nil]; NSLog(@"%@",file); NSArray *files = [fileManage subpathsAtPath: myDirectory ]; NSLog(@"%@",files); |
獲取上面剛纔test文件夾裏的文件名
打印結果
2012-06-17 23:23:19.684 IosSandbox[947:f803] fileList:( ".DS_Store", "test00.txt", "test22.txt", "test33.txt" ) 2012-06-17 23:23:19.686 IosSandbox[947:f803] fileLit( ".DS_Store", "test00.txt", "test22.txt", "test33.txt" ) |
兩個方法均可以,隱藏的文件也打印出來了。
四、fileManager使用操做當前目錄
//建立文件管理器 NSFileManager *fileManager = [NSFileManager defaultManager]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; //更改到待操做的目錄下 [fileManager changeCurrentDirectoryPath:[documentsDirectory stringByExpandingTildeInPath]]; //建立文件fileName文件名稱,contents文件的內容,若是開始沒有內容能夠設置爲nil,attributes文件的屬性,初始爲nil NSString * fileName = @"testFileNSFileManager.txt"; NSArray *array = [[NSArray alloc] initWithObjects:@"hello world",@"hello world1", @"hello world2",nil]; [fileManager createFileAtPath:fileName contents:array attributes:nil]; |
這樣就建立了testFileNSFileManager.txt並把三個hello world寫入文件了
changeCurrentDirectoryPath目錄更改到當前操做目錄時,作文件讀寫就很方便了,不用加上全路徑
五、刪除文件
接上面的代碼,remove就ok了。
[fileManager removeItemAtPath:fileName error:nil]; |
六、混合數據的讀寫
用NSMutableData建立混合數據,而後寫到文件裏。並按數據的類型把數據讀出來
6.1寫入數據:
NSString * fileName = @"testFileNSFileManager.txt"; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; //獲取文件路徑 NSString *path = [documentsDirectory stringByAppendingPathComponent:fileName]; //待寫入的數據 NSString *temp = @"nihao 世界"; int dataInt = 1234; float dataFloat = 3.14f; //建立數據緩衝 NSMutableData *writer = [[NSMutableData alloc] init]; //將字符串添加到緩衝中 [writer appendData:[temp dataUsingEncoding:NSUTF8StringEncoding]]; //將其餘數據添加到緩衝中 [writer appendBytes:&dataInt length:sizeof(dataInt)]; [writer appendBytes:&dataFloat length:sizeof(dataFloat)]; //將緩衝的數據寫入到文件中 [writer writeToFile:path atomically:YES]; |
咱們看看數據怎麼樣了:
咱們看到後面的是亂碼,那是中文被轉成了NSData後,還有int float的二進制
6.2讀取剛纔寫入的數據:
//讀取數據: int intData; float floatData = 0.0; NSString *stringData; NSData *reader = [NSData dataWithContentsOfFile:path]; stringData = [[NSString alloc] initWithData:[reader subdataWithRange:NSMakeRange(0, [temp length])] encoding:NSUTF8StringEncoding]; [reader getBytes:&intData range:NSMakeRange([temp length], sizeof(intData))]; [reader getBytes:&floatData range:NSMakeRange([temp length] + sizeof(intData), sizeof(floatData))]; NSLog(@"stringData:%@ intData:%d floatData:%f", stringData, intData, floatData); |
打印出來的結果:
2012-06-17 23:51:14.723 IosSandbox[1285:f803] stringData:nihao hello! intData:1234332 floatData:3.140000
這裏把寫入的漢字改爲了 hello。由於[temp length]算長度是,把中文算成一位了,出來的結果有誤。
屬性列表文件是一種XML文件,Foundation框架中的數組和字典等均可以於屬性列表文件相互轉換。
NSArray類經常使用讀寫屬性列表文件的方法:
+arrayWithContentsOfFile:類級構造方法,用於從屬性列表文件中讀取數據,建立NSArray對象。
-initWithContentsOfFile:實例構造方法,用於從屬性列表文件中讀取數據,建立NSArray對象。
-writeToFile:atomically:該方法把NSArray對象寫入到屬性列表文件中,第一個參數是文件名,第二個參數爲是否使用輔助文件,若是爲YES,則先寫入到一個輔助文件,而後輔助文件再從新命名爲目標文件,若是爲NO,則直接寫入到目標文件。
NSDictionary類經常使用讀寫屬性列表文件的方法:
+dictionaryWithContentsOfFile:類級構造方法,用於從屬性列表文件中讀取數據,建立NSDictionary對象。
-initWithContentsOfFile:實例構造方法,用於從屬性列表文件中讀取數據,建立NSDictionary對象。
-writeToFile:atomically:該方法將NSDictionary對象寫入到屬性列表文件中。
屬性列表文件數據持久化具體方法,可參考如下實現方式:
假如在項目中手工建立了一個Contacts.plist文件,並在該文件中添加了幾條數據,以下圖所示。
固然也能夠經過代碼直接建立plist文件。
接下來須要作的是將項目資源的Contacts.plist文件中數據複製到沙箱Documents目錄下。
//對文件進行預處理,判斷在Documents目錄下是否存在plist文件,若是不存在則從資源目錄下複製一個。 -(void)createEditableCopyOfDatabaseIfNeeded { NSFileManager *fileManager=[NSFileManager defaultManager]; NSString *writableDBPath=[self applicationDocumentsDirectoryFile]; BOOL dbexits=[fileManager fileExistsAtPath:writableDBPath]; if (!dbexits) { NSString *defaultDBPath=[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Contacts.plist"]; NSError *error; BOOL success=[fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error]; if (!success) { NSAssert1(0,@"錯誤寫入文件:‘%@’",[error localizedDescription]); } } } //獲取放置在沙箱Documents目錄下的文件的完整路徑 -(NSString *)applicationDocumentsDirectoryFile { NSString *documentDirectory=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *path=[documentDirectory stringByAppendingPathComponent:@"Contacts.plist"]; return path; }
createEditableCopyOfDatabaseIfNeeded方法中:
NSFileManager的copyItemAtPath:toPath:error:方法實現文件複製。
NSAssert1是Foundation框架提供的宏,它在斷言失敗的狀況下拋出異常,相似的還有NSAssert和NSAssert2等。
applicationDocumentsDirectoryFile方法中:
stringByAppendingPathComponent:可以在目錄後面追加文件名,返回完整的文件路徑。
沙箱Documents目錄下成功生成plist文件以後,就能夠進行增、刪、改、查操做了。可參考以下代碼:
NSString *path=[self applicationDocumentsDirectoryFile]; //將屬性列表文件內容讀取到array變量中,也就是獲取了屬性列表文件中所有的數據集合 NSMutableArray *array=[[NSMutableArray alloc]initWithContentsOfFile:path]; //向array中添加一條新記錄 NSDictionary *newContact=[NSDictionary dictionaryWithObjects:@[contact.Title,contact.Type] forKeys:@[@"Title",@"Type"]]; [array addObject:newContact]; //刪除array中一條記錄 [array removeObjectAtIndex:0]; //刪除array中所有記錄 [array removeAllObjects]; for (NSDictionary* dict in array) { //經過for循環,找到須要修改的數據項,進行修改數據 [dict setValue:@"Test" forKey:@"Title"]; } //將array從新寫入屬性列表文件中 [array writeToFile:path atomically:YES];
注:完成後,須要選擇Product->Clean菜單項清除一些再編譯。
iOS開發UI篇—ios應用數據存儲方式(歸檔)
1、簡單說明
2、代碼示例
1.文件結構
2.代碼示例
YYViewController.m文件
1 // 2 // YYViewController.m 3 // 02-歸檔 4 // 5 // Created by apple on 14-6-7. 6 // Copyright (c) 2015年 itcase. All rights reserved. 7 // 8 9 #import "YYViewController.h" 10 #import "YYPerson.h" 11 12 @interface YYViewController () 13 - (IBAction)saveBtnOnclick:(id)sender; 14 - (IBAction)readBtnOnclick:(id)sender; 15 16 @end 17 18 @implementation YYViewController 19 20 - (void)viewDidLoad 21 { 22 [super viewDidLoad]; 23 } 24 25 26 - (IBAction)saveBtnOnclick:(id)sender { 27 //1.建立對象 28 YYPerson *p=[[YYPerson alloc]init]; 29 p.name=@"iCocos"; 30 p.age=23; 31 p.height=1.7; 32 33 //2.獲取文件路徑 34 NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; 35 NSString *path=[docPath stringByAppendingPathComponent:@"person.yangyang"]; 36 NSLog(@"path=%@",path); 37 38 //3.將自定義的對象保存到文件中 39 [NSKeyedArchiver archiveRootObject:p toFile:path]; 40 41 } 42 43 - (IBAction)readBtnOnclick:(id)sender { 44 //1.獲取文件路徑 45 NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; 46 NSString *path=[docPath stringByAppendingPathComponent:@"person.yangyang"]; 47 NSLog(@"path=%@",path); 48 49 //2.從文件中讀取對象 50 YYPerson *p=[NSKeyedUnarchiver unarchiveObjectWithFile:path]; 51 NSLog(@"%@,%d,%.1f",p.name,p.age,p.height); 52 } 53 @end
新建一個person類
YYPerson.h文件
1 // 2 // YYPerson.h 3 // 02-歸檔 4 // 5 // Created by apple on 14-6-7. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 // 若是想將一個自定義對象保存到文件中必須實現NSCoding協議 12 @interface YYPerson : NSObject<NSCoding> 13 14 //姓名 15 @property(nonatomic,copy)NSString *name; 16 //年齡 17 @property(nonatomic,assign)int age; 18 //身高 19 @property(nonatomic,assign)double height; 20 @end
YYPerson.m文件
1 // 2 // YYPerson.m 3 // 02-歸檔 4 // 5 // Created by apple on 14-6-7. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYPerson.h" 10 11 @implementation YYPerson 12 13 // 當將一個自定義對象保存到文件的時候就會調用該方法 14 // 在該方法中說明如何存儲自定義對象的屬性 15 // 也就說在該方法中說清楚存儲自定義對象的哪些屬性 16 -(void)encodeWithCoder:(NSCoder *)aCoder 17 { 18 NSLog(@"調用了encodeWithCoder:方法"); 19 [aCoder encodeObject:self.name forKey:@"name"]; 20 [aCoder encodeInteger:self.age forKey:@"age"]; 21 [aCoder encodeDouble:self.height forKey:@"height"]; 22 } 23 24 // 當從文件中讀取一個對象的時候就會調用該方法 25 // 在該方法中說明如何讀取保存在文件中的對象 26 // 也就是說在該方法中說清楚怎麼讀取文件中的對象 27 -(id)initWithCoder:(NSCoder *)aDecoder 28 { 29 NSLog(@"調用了initWithCoder:方法"); 30 //注意:在構造方法中須要先初始化父類的方法 31 if (self=[super init]) { 32 self.name=[aDecoder decodeObjectForKey:@"name"]; 33 self.age=[aDecoder decodeIntegerForKey:@"age"]; 34 self.height=[aDecoder decodeDoubleForKey:@"height"]; 35 } 36 return self; 37 } 38 @end
3.打印效果和兩個重要的錯誤提示
點擊保存按鈕和讀取按鈕,成功打印結果以下:
關於不實現兩個協議方法的錯誤提示:
-(void)encodeWithCoder:(NSCoder *)aCoder方法:
-(id)initWithCoder:(NSCoder *)aDecoder方法:
3、繼承類中的使用
新建一個學生類,讓這個類繼承自Preson這個類,增長一個體重的屬性。
YYstudent.h文件
1 // 2 // YYstudent.h 3 // 02-歸檔 4 // 5 // Created by apple on 14-6-7. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYPerson.h" 10 11 @interface YYstudent : YYPerson 12 //增長一個體重屬性 13 @property(nonatomic,assign) double weight; 14 @end
YYstudent.m文件
1 // 2 // YYstudent.m 3 // 02-歸檔 4 // 5 // Created by apple on 14-6-7. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYstudent.h" 10 11 @implementation YYstudent 12 13 //在子類中重寫這兩個方法 14 - (void)encodeWithCoder:(NSCoder *)aCoder 15 { 16 [super encodeWithCoder:aCoder]; 17 NSLog(@"調用了YYStudent encodeWithCoder"); 18 [aCoder encodeFloat:self.weight forKey:@"weight"]; 19 } 20 21 - (id)initWithCoder:(NSCoder *)aDecoder 22 { 23 if (self = [super initWithCoder:aDecoder]) { 24 NSLog(@"調用了YYstudent initWithCoder"); 25 self.weight = [aDecoder decodeFloatForKey:@"weight"]; 26 } 27 return self; 28 } 29 @end
YYViewController.m文件
1 // 2 // YYViewController.m 3 // 02-歸檔 4 // 5 // Created by apple on 14-6-7. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYViewController.h" 10 #import "YYPerson.h" 11 #import "YYstudent.h" 12 13 @interface YYViewController () 14 - (IBAction)saveBtnOnclick:(id)sender; 15 - (IBAction)readBtnOnclick:(id)sender; 16 17 @end 18 19 @implementation YYViewController 20 21 - (void)viewDidLoad 22 { 23 [super viewDidLoad]; 24 } 25 26 27 - (IBAction)saveBtnOnclick:(id)sender { 28 //1.建立對象 29 // YYPerson *p=[[YYPerson alloc]init]; 30 // p.name=@"文頂頂"; 31 // p.age=23; 32 // p.height=1.7; 33 34 YYstudent *s=[[YYstudent alloc]init]; 35 s.name=@"wendingding"; 36 s.age=23; 37 s.height=1.7; 38 s.weight=62; 39 //2.獲取文件路徑 40 NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; 41 NSString *path=[docPath stringByAppendingPathComponent:@"person.yangyang"]; 42 NSLog(@"path=%@",path); 43 44 //3.將自定義的對象保存到文件中 45 // [NSKeyedArchiver archiveRootObject:p toFile:path]; 46 [NSKeyedArchiver archiveRootObject:s toFile:path]; 47 48 } 49 50 - (IBAction)readBtnOnclick:(id)sender { 51 //1.獲取文件路徑 52 NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; 53 NSString *path=[docPath stringByAppendingPathComponent:@"person.yangyang"]; 54 NSLog(@"path=%@",path); 55 56 //2.從文件中讀取對象 57 // YYPerson *p=[NSKeyedUnarchiver unarchiveObjectWithFile:path]; 58 // NSLog(@"%@,%d,%.1f",p.name,p.age,p.height); 59 YYstudent *s=[NSKeyedUnarchiver unarchiveObjectWithFile:path]; 60 NSLog(@"%@,%d,%.1f,%f",s.name,s.age,s.height,s.weight); 61 } 62 @end
點擊保存按鈕和讀取按鈕後的打印輸出:
4、重要說明
1.保存數據過程:
//1.建立對象 YYstudent *s=[[YYstudent alloc]init]; s.name=@"wendingding"; s.age=23; s.height=1.7; s.weight=62; //2.獲取文件路徑 NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; NSString *path=[docPath stringByAppendingPathComponent:@"person.yangyang"]; NSLog(@"path=%@",path); //3.將自定義的對象保存到文件中 [NSKeyedArchiver archiveRootObject:s toFile:path];
2.讀取數據過程:
//1.獲取文件路徑 NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; NSString *path=[docPath stringByAppendingPathComponent:@"person.yangyang"]; //2.從文件中讀取對象 YYstudent *s=[NSKeyedUnarchiver unarchiveObjectWithFile:path];
3.遵照NSCoding協議,並實現該協議中的兩個方法。
4.若是是繼承,則子類必定要重寫那兩個方法。由於person的子類在存取的時候,會去子類中去找調用的方法,沒找到那麼它就去父類中找,因此最後保存和讀取的時候新增長的屬性會被忽略。須要先調用父類的方法,先初始化父類的,再初始化子類的。
5.保存數據的文件的後綴名能夠隨意命名。
6.經過plist保存的數據是直接顯示的,不安全。經過歸檔方法保存的數據在文件中打開是亂碼的,更安全。
偏好設置
1、簡單介紹
不少iOS應用都支持偏好設置,好比保存用戶名、密碼、字體大小等設置,iOS提供了一套標準的解決方案來爲應用加入偏好設置功能
每一個應用都有個NSUserDefaults實例,經過它來存取偏好設置。好比,保存用戶名、字體大小、是否自動登陸
存儲位置:
存儲形式:
2、代碼示例
1.storyboard
2.代碼
1 // 2 // YYViewController.m 3 // 01-偏好設置 4 // 5 // Created by apple on 14-6-7. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYViewController.h" 10 //偏好設置 11 @interface YYViewController () 12 /** 13 *保存數據 14 */ 15 - (IBAction)saveData:(id)sender; 16 /** 17 * 讀取數據 18 */ 19 - (IBAction)readData:(id)sender; 20 21 @end 22 23 @implementation YYViewController 24 25 - (IBAction)saveData:(id)sender { 26 //1.獲取NSUserDefaults對象 27 NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; 28 29 //2保存數據(若是設置數據以後沒有同步, 會在未來某一時間點自動將數據保存到Preferences文件夾下面) 30 [defaults setObject:@"yangyong" forKey:@"name"]; 31 [defaults setInteger:23 forKey:@"age"]; 32 [defaults setDouble:1.73f forKey:@"height"]; 33 [defaults setObject:@"man" forKey:@"gender"]; 34 35 //3.強制讓數據馬上保存 36 [defaults synchronize]; 37 } 38 39 - (IBAction)readData:(id)sender { 40 //1.獲取NSUserDefaults對象 41 NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; 42 //讀取保存的數據 43 NSString *name=[defaults objectForKey:@"name"]; 44 NSString *gender=[defaults objectForKey:@"gender"]; 45 NSInteger age=[defaults integerForKey:@"age"]; 46 double height=[defaults doubleForKey:@"height"]; 47 //打印數據 48 NSLog(@"name=%@,gender=%@,age=%d,height=%.1f",name,gender,age,height); 49 } 50 @end
3.點擊保存數據,讀取數據按鈕打印以下
3、補充說明
1.保存數據
//1.獲取NSUserDefaults對象 NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; //2保存數據 [defaults setObject:@"yangyong" forKey:@"name"]; [defaults setInteger:23 forKey:@"age"]; [defaults setDouble:1.73f forKey:@"height"]; [defaults setObject:@"man" forKey:@"gender"]; //3.強制讓數據馬上保存 [defaults synchronize];
2.讀取數據
//1.獲取NSUserDefaults對象 NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; //2.讀取保存的數據 NSString *name=[defaults objectForKey:@"name"]; NSString *gender=[defaults objectForKey:@"gender"]; NSInteger age=[defaults integerForKey:@"age"]; double height=[defaults doubleForKey:@"height"];
3.重要說明
(1)偏好設置是專門用來保存應用程序的配置信息的, 通常狀況不要在偏好設置中保存其餘數據。若是利用系統的偏好設置來存儲數據, 默認就是存儲在Preferences文件夾下面的,偏好設置會將全部的數據都保存到同一個文件中。
(2)使用偏好設置對數據進行保存以後, 它保存到系統的時間是不肯定的,會在未來某一時間點自動將數據保存到Preferences文件夾下面,若是須要即刻將數據存儲,可使用[defaults synchronize];
(3)注意點:全部的信息都寫在一個文件中,對比簡單的plist能夠保存和讀取基本的數據類型。
(4)步驟:獲取NSuserDefaults,保存(讀取)數據
SQLite3詳解
SQLite是嵌入式的和輕量級的SQL數據庫。SQLite是由C實現的。普遍用於包括瀏覽器(支持HTML5的大部分瀏覽器,IE除外)、iOS、Android以及一些便攜需求的小型web應用系統。
1 使用緣由:存儲、檢索信息
2 SQLite是MySQL精簡版。但無需服務器就能進行。
3 兩個限制:1)必須手動建立數據庫 2)沒有面向對象的接口。
4 如何手動建立數據庫。
使用SQLite前的準備
使用SQLite是不少作iOS開發中第一次面對C的狀況,包括我。由於SQLite是C寫的,Objective-C能夠直接使用C代碼。在SQLite前,通常都會使用Cocoa Touch框架,都是基於Objective-C的。
首先,添加framework:libsqlite3.0.dylib
須要在對應文件的頭文件中加入:
#import "sqlite3.h" |
並在Frameworks中加入所需的庫,不然會報錯:
Undefined symbols: "_sqlite3_open", referenced from: |
加入庫的方法是:
1 static NoteDAO *sharedManager = nil; 2 3 + (NoteDAO*)sharedManager 4 { 5 static dispatch_once_t once; 6 dispatch_once(&once, ^{ 7 8 sharedManager = [[self alloc] init]; 9 [sharedManager createEditableCopyOfDatabaseIfNeeded]; 10 11 12 }); 13 return sharedManager; 14 } 15 16 //打開數據庫 17 - (void)createEditableCopyOfDatabaseIfNeeded { 18 19 NSString *writableDBPath = [self applicationDocumentsDirectoryFile]; 20 21 if (sqlite3_open([writableDBPath UTF8String], &db) != SQLITE_OK) { 22 sqlite3_close(db); 23 NSAssert(NO,@"數據庫打開失敗。"); 24 } else { 25 char *err; 26 NSString *createSQL = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS Note (cdate TEXT PRIMARY KEY, content TEXT);"]; 27 if (sqlite3_exec(db,[createSQL UTF8String],NULL,NULL,&err) != SQLITE_OK) { 28 sqlite3_close(db); 29 NSAssert1(NO, @"建表失敗, %s", err); 30 } 31 sqlite3_close(db); 32 } 33 } 34 35 - (NSString *)applicationDocumentsDirectoryFile { 36 NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 37 NSString *path = [documentDirectory stringByAppendingPathComponent:DBFILE_NAME]; 38 39 return path; 40 } 41 42 43 //插入Note方法 44 -(int) create:(Note*)model 45 { 46 47 NSString *path = [self applicationDocumentsDirectoryFile]; 48 49 if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { 50 sqlite3_close(db); 51 NSAssert(NO,@"數據庫打開失敗。"); 52 } else { 53 54 NSString *sqlStr = @"INSERT OR REPLACE INTO note (cdate, content) VALUES (?,?)"; 55 56 sqlite3_stmt *statement; 57 //預處理過程 58 if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) { 59 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 60 [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 61 NSString *nsdate = [dateFormatter stringFromDate:model.date]; 62 63 //綁定參數開始 64 sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL); 65 sqlite3_bind_text(statement, 2, [model.content UTF8String], -1, NULL); 66 67 //執行插入 68 if (sqlite3_step(statement) != SQLITE_DONE) { 69 NSAssert(NO, @"插入數據失敗。"); 70 } 71 } 72 73 sqlite3_finalize(statement); 74 sqlite3_close(db); 75 } 76 77 return 0; 78 } 79 80 //刪除Note方法 81 -(int) remove:(Note*)model 82 { 83 NSString *path = [self applicationDocumentsDirectoryFile]; 84 85 if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { 86 sqlite3_close(db); 87 NSAssert(NO,@"數據庫打開失敗。"); 88 } else { 89 90 NSString *sqlStr = @"DELETE from note where cdate =?"; 91 92 sqlite3_stmt *statement; 93 //預處理過程 94 if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) { 95 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 96 [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 97 NSString *nsdate = [dateFormatter stringFromDate:model.date]; 98 99 //綁定參數開始 100 sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL); 101 //執行 102 if (sqlite3_step(statement) != SQLITE_DONE) { 103 NSAssert(NO, @"刪除數據失敗。"); 104 } 105 } 106 107 sqlite3_finalize(statement); 108 sqlite3_close(db); 109 } 110 111 return 0; 112 } 113 114 //修改Note方法 115 -(int) modify:(Note*)model 116 { 117 118 NSString *path = [self applicationDocumentsDirectoryFile]; 119 120 if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { 121 sqlite3_close(db); 122 NSAssert(NO,@"數據庫打開失敗。"); 123 } else { 124 125 NSString *sqlStr = @"UPDATE note set content=? where cdate =?"; 126 127 sqlite3_stmt *statement; 128 //預處理過程 129 if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) { 130 131 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 132 [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 133 NSString *nsdate = [dateFormatter stringFromDate:model.date]; 134 135 //綁定參數開始 136 sqlite3_bind_text(statement, 1, [model.content UTF8String], -1, NULL); 137 sqlite3_bind_text(statement, 2, [nsdate UTF8String], -1, NULL); 138 //執行 139 if (sqlite3_step(statement) != SQLITE_DONE) { 140 NSAssert(NO, @"修改數據失敗。"); 141 } 142 } 143 144 sqlite3_finalize(statement); 145 sqlite3_close(db); 146 } 147 return 0; 148 } 149 150 //查詢全部數據方法 151 -(NSMutableArray*) findAll 152 { 153 154 NSString *path = [self applicationDocumentsDirectoryFile]; 155 NSMutableArray *listData = [[NSMutableArray alloc] init]; 156 157 if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { 158 sqlite3_close(db); 159 NSAssert(NO,@"數據庫打開失敗。"); 160 } else { 161 162 NSString *qsql = @"SELECT cdate,content FROM Note"; 163 164 sqlite3_stmt *statement; 165 //預處理過程 166 if (sqlite3_prepare_v2(db, [qsql UTF8String], -1, &statement, NULL) == SQLITE_OK) { 167 168 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 169 [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 170 171 //執行 172 while (sqlite3_step(statement) == SQLITE_ROW) { 173 char *cdate = (char *) sqlite3_column_text(statement, 0); 174 NSString *nscdate = [[NSString alloc] initWithUTF8String: cdate]; 175 176 char *content = (char *) sqlite3_column_text(statement, 1); 177 NSString * nscontent = [[NSString alloc] initWithUTF8String: content]; 178 179 Note* note = [[Note alloc] init]; 180 note.date = [dateFormatter dateFromString:nscdate]; 181 note.content = nscontent; 182 183 [listData addObject:note]; 184 185 } 186 } 187 188 sqlite3_finalize(statement); 189 sqlite3_close(db); 190 191 } 192 return listData; 193 } 194 195 //按照主鍵查詢數據方法 196 -(Note*) findById:(Note*)model 197 { 198 199 NSString *path = [self applicationDocumentsDirectoryFile]; 200 201 if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { 202 sqlite3_close(db); 203 NSAssert(NO,@"數據庫打開失敗。"); 204 } else { 205 206 NSString *qsql = @"SELECT cdate,content FROM Note where cdate =?"; 207 208 sqlite3_stmt *statement; 209 //預處理過程 210 if (sqlite3_prepare_v2(db, [qsql UTF8String], -1, &statement, NULL) == SQLITE_OK) { 211 //準備參數 212 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 213 [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 214 NSString *nsdate = [dateFormatter stringFromDate:model.date]; 215 //綁定參數開始 216 sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL); 217 218 //執行 219 if (sqlite3_step(statement) == SQLITE_ROW) { 220 char *cdate = (char *) sqlite3_column_text(statement, 0); 221 NSString *nscdate = [[NSString alloc] initWithUTF8String: cdate]; 222 223 char *content = (char *) sqlite3_column_text(statement, 1); 224 NSString * nscontent = [[NSString alloc] initWithUTF8String: content]; 225 226 Note* note = [[Note alloc] init]; 227 note.date = [dateFormatter dateFromString:nscdate]; 228 note.content = nscontent; 229 230 sqlite3_finalize(statement); 231 sqlite3_close(db); 232 233 return note; 234 } 235 } 236 237 sqlite3_finalize(statement); 238 sqlite3_close(db); 239 240 } 241 return nil; 242 }
注:SQLite中也沒有定義日期時間類型,日期時間能夠用TEXT,REAL,orINTEGER存儲
TEXT:存儲爲字符串("YYYY-MM-DDHH:MM:SS.SSS").
REAL:asJuliandaynumbers,thenumberofdayssincenooninGreenwichonNovember24,4714B.C.accordingtotheprolepticGregoriancalendar.
INTEGER:asUnixTime,thenumberofsecondssince1970-01-0100:00:00UTC.
SQLiteTypeAffinity(類型檢測)
用於自動檢測值的類型,如下列舉Affinity如何決定類型的規則
(1)若是類型聲明中有int,則使用INTEGERaffinity.
(2)若是類型聲明中有"CHAR","CLOB",or"TEXT",則使用Textaffinity
(3)若是類型聲明中有BLOB或沒有指定類型,則使用affinityNONE
http://mobile.51cto.com/iphone-321872.htm
(4)若是類型聲明中有"REAL","FLOA",or"DOUB",則使用REALaffinity
(5)不然使用Numericaffinity
類型比較NULL
memcmp函數原型
intmemcmp(constvoid*ptr1,constvoid*ptr2,size_tnum);
比較兩個指針指向內存的前num個byte
比較以前的類型轉換
l(INTEGER,REALorNUMERIC)和(TEXTorNONE)比較,則TEXT,NONE會被轉換成NUMERIC
lTEXT和NONE比較,則NONE會被轉換成TEXT
其餘狀況直接比較。
iOS SQLite3具備的數據類型
NULL:NULLvalue
Integer:值是signedinteger類型,大小能夠是1,2,3,4,6,8bytes
REAL:浮點類型
TEXT:以UTF-8,UTF-16BEorUTF-16LE編碼存儲的字符類型
BLOB:二進制數據
其它數據類型說明
在Cocoa環境下,若是你想使用數據庫(如sqlite),你可使用sql語句的方式經過相關的工具類進行數據庫的直接操做。固然你也能夠經過別人封裝以後的一些簡單框架,使得你的操做更加簡單(如FMDB BNRPersistence)。
Cocoa框架自己提供了CoreData這個API可方便的讓開發者經過操做對象的方式在操做數據庫。CoreData是一個對象圖(object graph)以及持久化的管理框架。咱們能夠經過CoreData創對象,設置好象之間的關係,而後將其持久化(咱們甚至可使用內存數據庫),或者從硬盤上將持久化後的數據加載到內存中。對象圖,咱們能夠建立一個個的對象,並維持不一樣對象之間的關係,一對一,一對多等。
CoreData有大量的特性,諸如支持Redo,Undo的功能,這些不少Document based的程序中顯得很是的有用。提供數據model結構變化輕量級的遷移方案。CoreData還經過Binding特性和控件的緊密結合,這樣使得只須要少許的代碼即可以完成強大的功能,下面是一個例子
http://www.timisted.net/blog/archive/multiple-windows-with-core-data/
Core Data能夠將數據存儲爲XML,二進制文件或SQLite文件。在Mac OS X 10.5 Leopard及之後的版本中,開發者也能夠經過繼承NSPersistentStore類以建立自定義的存儲格式。每種方法都有其優缺點,例如XML的可讀性,SQLite的節約空間等。
Core Data的這一方面相似於原始的Enterprise Objects Framework(EOF)系統,但EOF中開發者可使用相對簡潔的查詢方式,而在Core Data中,只能使用一個語法相似SQL子集的查詢語言,稱爲Predicate。Core Data是標準化的,能夠自由的讀寫Xcode數據模型文件(一般是.xcdatamodel文件)。
與EOF不一樣,Core Data目前沒有設計多用戶或多線程訪問模式。模型遷移一般也須要代碼,若其它開發者依賴於某個數據模型,則該數據模型的設計者可能在模型發生改變時須要與新數據模型一塊兒提供版本轉換代碼。
Core Data由相對龐大的類繼承體系組成,但開發者須要關注的接口只是其中的一個相對小的子集。
通常須要定義如下Core Data的三個必備
NSPersistentStoreCoordinator *persistentStoreCoordinator;
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
以及使用時須要用到的
NSFetchedResultsController *fetchedResultsController;
使用步驟:
還記得咱們每次使用CoreData的時候系統都會給咱們建立一些代碼嗎?
1 #pragma mark - Core Data 堆棧 2 //返回 被管理的對象上下文 3 - (NSManagedObjectContext *)managedObjectContext 4 { 5 if (_managedObjectContext) { 6 return _managedObjectContext; 7 } 8 9 NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 10 if (coordinator) { 11 _managedObjectContext = [[NSManagedObjectContext alloc] init]; 12 [_managedObjectContext setPersistentStoreCoordinator:coordinator]; 13 } 14 return _managedObjectContext; 15 } 16 17 // 返回 持久化存儲協調者 18 - (NSPersistentStoreCoordinator *)persistentStoreCoordinator 19 { 20 if (_persistentStoreCoordinator) { 21 return _persistentStoreCoordinator; 22 } 23 24 NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataNotes.sqlite"]; 25 26 _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 27 28 [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 29 configuration:nil 30 URL:storeURL 31 options:nil 32 error:nil]; 33 34 return _persistentStoreCoordinator; 35 } 36 37 // 返回 被管理的對象模型 38 - (NSManagedObjectModel *)managedObjectModel 39 { 40 if (_managedObjectModel) { 41 return _managedObjectModel; 42 } 43 NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreDataNotes" withExtension:@"momd"]; 44 _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; 45 return _managedObjectModel; 46 } 47 48 #pragma mark - 應用程序沙箱 49 // 返回應用程序Docment目錄的NSURL類型 50 - (NSURL *)applicationDocumentsDirectory 51 { 52 return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; 53 }
下面是使用Core的示例代碼
1 static NoteDAO *sharedManager = nil; 2 3 + (NoteDAO*)sharedManager 4 { 5 static dispatch_once_t once; 6 dispatch_once(&once, ^{ 7 8 sharedManager = [[self alloc] init]; 9 [sharedManager managedObjectContext]; 10 11 }); 12 return sharedManager; 13 } 14 15 16 //插入Note方法 17 -(int) create:(Note*)model 18 { 19 20 NSManagedObjectContext *cxt = [self managedObjectContext]; 21 22 NoteManagedObject *note = [NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:cxt]; 23 [note setValue: model.content forKey:@"content"]; 24 [note setValue: model.date forKey:@"date"]; 25 26 note.date = model.date; 27 note.content = model.content; 28 29 NSError *savingError = nil; 30 if ([self.managedObjectContext save:&savingError]){ 31 NSLog(@"插入數據成功"); 32 } else { 33 NSLog(@"插入數據失敗"); 34 return -1; 35 } 36 37 return 0; 38 } 39 40 //刪除Note方法 41 -(int) remove:(Note*)model 42 { 43 44 NSManagedObjectContext *cxt = [self managedObjectContext]; 45 46 NSEntityDescription *entityDescription = [NSEntityDescription 47 entityForName:@"Note" inManagedObjectContext:cxt]; 48 49 NSFetchRequest *request = [[NSFetchRequest alloc] init]; 50 [request setEntity:entityDescription]; 51 52 NSPredicate *predicate = [NSPredicate predicateWithFormat: 53 @"date = %@", model.date]; 54 [request setPredicate:predicate]; 55 56 NSError *error = nil; 57 NSArray *listData = [cxt executeFetchRequest:request error:&error]; 58 if ([listData count] > 0) { 59 NoteManagedObject *note = [listData lastObject]; 60 [self.managedObjectContext deleteObject:note]; 61 62 NSError *savingError = nil; 63 if ([self.managedObjectContext save:&savingError]){ 64 NSLog(@"刪除數據成功"); 65 } else { 66 NSLog(@"刪除數據失敗"); 67 return -1; 68 } 69 } 70 71 return 0; 72 } 73 74 //修改Note方法 75 -(int) modify:(Note*)model 76 { 77 NSManagedObjectContext *cxt = [self managedObjectContext]; 78 79 NSEntityDescription *entityDescription = [NSEntityDescription 80 entityForName:@"Note" inManagedObjectContext:cxt]; 81 82 NSFetchRequest *request = [[NSFetchRequest alloc] init]; 83 [request setEntity:entityDescription]; 84 85 NSPredicate *predicate = [NSPredicate predicateWithFormat: 86 @"date = %@", model.date]; 87 [request setPredicate:predicate]; 88 89 NSError *error = nil; 90 NSArray *listData = [cxt executeFetchRequest:request error:&error]; 91 if ([listData count] > 0) { 92 NoteManagedObject *note = [listData lastObject]; 93 note.content = model.content; 94 95 NSError *savingError = nil; 96 if ([self.managedObjectContext save:&savingError]){ 97 NSLog(@"修改數據成功"); 98 } else { 99 NSLog(@"修改數據失敗"); 100 return -1; 101 } 102 } 103 return 0; 104 } 105 106 //查詢全部數據方法 107 -(NSMutableArray*) findAll 108 { 109 NSManagedObjectContext *cxt = [self managedObjectContext]; 110 111 NSEntityDescription *entityDescription = [NSEntityDescription 112 entityForName:@"Note" inManagedObjectContext:cxt]; 113 114 NSFetchRequest *request = [[NSFetchRequest alloc] init]; 115 [request setEntity:entityDescription]; 116 117 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES]; 118 [request setSortDescriptors:@[sortDescriptor]]; 119 120 NSError *error = nil; 121 NSArray *listData = [cxt executeFetchRequest:request error:&error]; 122 123 NSMutableArray *resListData = [[NSMutableArray alloc] init]; 124 125 for (NoteManagedObject *mo in listData) { 126 Note *note = [[Note alloc] init]; 127 note.date = mo.date; 128 note.content = mo.content; 129 [resListData addObject:note]; 130 } 131 132 return resListData; 133 } 134 135 //按照主鍵查詢數據方法 136 -(Note*) findById:(Note*)model 137 { 138 NSManagedObjectContext *cxt = [self managedObjectContext]; 139 140 NSEntityDescription *entityDescription = [NSEntityDescription 141 entityForName:@"Note" inManagedObjectContext:cxt]; 142 143 NSFetchRequest *request = [[NSFetchRequest alloc] init]; 144 [request setEntity:entityDescription]; 145 146 NSPredicate *predicate = [NSPredicate predicateWithFormat: 147 @"date = %@",model.date]; 148 [request setPredicate:predicate]; 149 150 NSError *error = nil; 151 NSArray *listData = [cxt executeFetchRequest:request error:&error]; 152 153 if ([listData count] > 0) { 154 NoteManagedObject *mo = [listData lastObject]; 155 156 Note *note = [[Note alloc] init]; 157 note.date = mo.date; 158 note.content = mo.content; 159 160 return note; 161 } 162 return nil; 163 }
CoreData高級常識
關於CoreData貌似實際開發中不多用到,基本上是個有九個公司不會使用它,由於都說是性能很差,可是做爲一個程序員,瞭解及其使用時必須了,
下面是我從一位大神那裏搬過來的一下Core詳細介紹,相信之後總有一天會幫我解決很多學習CoreData中的問題!
1、技術概覽
1. Core Data 功能初窺
對於處理諸如對象生命週期管理、對象圖管理等平常任務,Core Data框架提供了普遍且自動化的解決方案。它有如下特性。
(注:對象圖-Object graph的解釋:在面向對象編程中,對象之間有各類關係,例如對象直接引用另外的對象,或是經過引用鏈間接的引用其餘對象,這些關係組成了網狀的結構。 咱們把這些對象(和它們之間的聯繫)成爲對象圖。 對象圖可大可小,有繁有簡。 只包含單個字符串對象的數組就是一個簡單的表明;而包含了application對象,引用windows, menus和相關視圖對象、其餘對象這樣的結構就是複雜對象圖的例子——這是在說mainwindow.xib。
有時,你可能想要把這樣的對象圖轉化形式,讓它們能夠被保存到文件中,以使其餘的進程或其餘的機器能夠再次將保存的內容讀出,重購對象。 這樣的過程常被成之爲「歸檔」(Archiving)。
有些對象圖是不完整的——一般稱之爲局部對象圖(partial object graphs)。局部對象圖包含了「佔位符」(Placeholder)對象,所謂」佔位符「,就是一些暫時無內容的對象,它們將再後期被具體化。一個典 型的例子就是nib文件中包含的File's Owner對象。
1) 對於key-value coding 和key-value observing完整且自動化的支持
除了爲屬性整合KVC和KVO的訪問方法外, Core Data還整合了適當的集合訪問方法來處理多值關係。
2) 自動驗證屬性(property)值
Core Data中的managed object擴展了標準的KVC 驗證方法,以保證單個的數值在可接受的範圍以內,從而使組合的值有意義。(需校準翻譯)
3) 支持跟蹤修改和撤銷操做
對於撤銷和重作的功能,除過用基本的文本編輯外,Core Data還提供內置的管理方式。
4) 關係的維護
Core Data管理數據的變化傳播,包括維護對象間關係的一致性。
5) 在內存中和界面上分組、過濾、組織數據
6) 自動支持對象存儲在外部數據倉庫的功能
7) 建立複雜請求
你不須要動手去寫複雜的SQL語句,就能夠建立複雜的數據請求。方法是在「獲取請求」(fetch request)中關聯NSPredicate(又看到這個東東了,以前用它作過正則)。NSPrdicate支持基本的功能、相關子查詢和其餘高級的 SQL特性。它還支持正確的Unicode編碼(不太懂,請高人指點), 區域感知查詢(聽說就是根據區域、語言設置調整查詢的行爲)、排序和正則表達式。
8) 延遲操做(原文爲Futures(faulting)直譯爲期貨,這裏我的感受就是延遲操做的形象說法。請高人指教)。
Core Data 使用延遲加載(lazy loading)的方式減小內存負載。 它還支持部分實體化延遲加載,和「寫時拷貝」的數據共享機制。(寫時拷貝,說的是在複製對象的時候,實際上不生成新的空間,而是讓對象共享一塊存儲區域, 在其內容發生改變的時候再分配)。
9) 合併的策略
Core Data 內置了版本跟蹤和樂觀鎖定(optimistic locking)來支持多用戶寫入衝突的解決。
注:樂觀鎖,假定數據通常不出現衝突,因此在數據提交更新的時候,纔對數據的衝突進行檢測,若是衝突了,就返回衝突信息。
10) 數據遷移
就開發工做和運行時資源來講,處理數據庫架構的改變老是很複雜。Core Data的schema migration工具能夠簡化應對數據庫結構變化的任務, 並且在某些狀況下,容許你執行高效率的數據庫原地遷移工做。
11) 可選擇針對程序Controller層的集成,來支持UI的顯示同步
Core Data在iPhone OS之上 提供NSFetchedResultsController對象來作相關工做,在Mac OS X上,咱們用Cocoa提供的綁定(Binding)機制來完成。
2. 爲什麼要使用Core Data
使用Core Data有不少緣由,其中最簡單的一條就是:它能讓你爲Model層寫的代碼的行數減小爲原來的50%到70%。 這歸功於以前提到的Core Data的特性。更妙的是,對於上述特性你也既不用去測試,也不用花功夫去優化。
Core Data擁有成熟的代碼,這些代碼經過單元測試來保證品質。應用Core Data的程序天天被世界上幾百萬用戶使用。經過了幾個版本的發佈,已經被高度優化。 它能利用Model層的信息和運行時的特性,而不經過程序層的代碼實現。 除了提供強大的安全支持和錯誤處理外,它還提供了最優的內存擴展性,可實現有競爭力的解決方案。不使用Core Data的話,你須要花很長時間來起草本身的方案,解決各類問題,這樣作效率不高。
除了Core Data自己的優勢以外,使用它還有其餘的好處: 它很容易和Mac OS X系統的Tool chain集成;利用Model設計工具能夠按圖形化方式輕鬆建立數據庫的結構;你能夠用Instruments的相關模板來測試Core Data的效率並debug。 在Mac OS X的桌面程序中,Core Data還和Interface Builder集成(打開Inspector能夠看到有binding的選項,這個東東iPhone上木有。。。),按照model來建立UI變的更簡單 了。 這些功能能更進一步的幫助你縮短設計、開發、測試程序的週期。
3. Core Data不是。。。
看了前面的介紹以後,咱們還須要瞭解一下關於Core Data常見的誤解:
1) Core Data不是一個關係型數據庫,也不是關係型數據庫管理系統(RDBMS)。
Core Data 爲數據變動管理、對象存儲、對象讀取恢復的功能提供了支持。 它可使用SQLite做爲持久化存儲的類型。 它自己並非一個數據庫(這點很重要,好比,你可使用Core Data來記錄數據變動,管理數據,但並不能用它向文件內存儲數據)。
2) Core Data不是銀彈
它並不能取代你寫代碼的工做。雖然能夠純粹使用XCode的數據建模工具和Interface Builder來編寫複雜程序,但在更多的程序中,你都本身動手寫代碼。
3) Core Data並不依賴於Cocoa Bindings
Core Data + Cocoa Binding = 減小代碼數量。但Core Data徹底能夠在沒有bindings的條件下使用。例如,能夠編寫一個沒有UI,但包含Core Data的程序。
2、Core Data基礎
1. Core Data基本架構
在大部分程序中,你要能經過某種方式打開一個包含對象歸檔的文件, 這個文件內至少要有一個根對象的引用。另外,還得能將全部的對象歸檔到文件中,若是你想要實現撤銷的功能,就還要記錄對象的更改狀況。例如,在 Employee的示例程序中,你要能打開一個包含有employee和department對象歸檔的文件,並且這個文件至少包含了一個根對象——這 裏,是一個包含全部employee的數組——請參考例圖Figure 1。 相應的,你還要能將程序中的employee、department對象歸檔到文件中去。
Figure 1 按照Core Data文檔結構管理的對象示意圖
使用Core Data的框架,大多數的功能均可以自動實現,由於咱們有managed object context(管理對象的上下文,有時直接叫"Context")。managed object context就像是一個關卡,經過它能夠訪問框架底層的對象——這些對象的集合咱們稱之爲"persistence stack"(數據持久棧)。 managed object context做爲程序中對象和外部的數據存儲的中轉站。棧的底部是persistence object stores(持久化數據存儲),請看Figure 2的示意圖。
Figure 2 使用Core Data的文檔管理示意圖
Core Data的使用並不限制在基於文檔的程序中(document-based application)。你也能建立一個包含Core Data 的Utility程序(請查看Core Data Utility tutorial文檔)。固然其餘類型的程序也均可以使用Core Data。
被管理對象和上下文(Managed Objects and Contexts)
你能夠把被管理對象上下文想象成一個」聰明「的便箋簿。當你從數據持久層獲取對象時,就把這些臨時的數據拷貝拿到寫在本身的便箋簿上(固然,在便箋上對象 會 「恢復」之前的對象圖結構)。而後你就能夠爲所欲爲的修改這些值了(本子是你的,隨便畫均可以),除非你保存這些數據變化,不然持久層的東西是不會變 的。(跟修改文件後要保存是一個道理)。
附在Core Data框架中模型對象(Model objects)常被稱爲「被管理對象」(Managed objects)。全部的被管理對象都要經過上下文進行註冊。使用上下文,你能夠在對象圖中添加、刪除對象,並記錄對象的更改(包括單個對象,或是對象間 的關係)。記錄更改後就能支持撤銷和重作的功能。同時,上下文還能保證關係更改後對象圖的完整性。
若是你想要保存所作的修改, 上下文會保證對象的有效性。在驗證有效性後,更改會被寫入到persistent store(持久化存儲層)中。你在程序中的添加和刪除動做都會被做用在存儲的數據中。
在你的一個程序中,可能存在多個上下文。 對於數據存儲(store)中的每一個對象,對應的都有惟一的一個被管理對象(managed object)和上下文相關聯(詳情請查看"Faulting and Uniquing"文檔)。換個角度來想,在persistent store中存儲的對象有可能被用在不一樣的上下文中,每一個上下文都有與之對應的被管理對象,被管理對象能夠被獨立的修改,這樣就可能在存儲 時致使數據的不一致。Core Data提供了許多解決這個問題的途徑(請查看"Using Managed Object"一章)。
獲取數據的請求(Fetch Requests)
要使用上下文來獲取數據,你須要建立相應的請求(Fetch request)。 Fetch request對象包含你想獲取的對象的描述。例如:「全部 Employee」,或「全部的Employee,department是marketing,按薪資降序排列」。Fetch Request包含三個部分。使用最簡單的寫法,必須指定實體(Entity)的名稱,這就暗示了,每次智能得到一種類型的實體。 Fetch Request 還能夠包含謂詞(predicate)——注:有些地方也把這個叫斷言,我的感受謂詞更準確些。謂詞將描述對象須要知足的條件(這就和咱們在SQL里加的 限定條件差很少,正如前面的"All Employees, in the Marketing department")。另外,Fetch Request還可包含一個用於描述排序方式的對象(熟悉的Order by操做)。如圖Figure3所示:
Core Data追求高執行效率。 它是「需求驅動」的,所以只會建立你確實須要的對象。對象圖不須要保留全部在數據存儲層中的對象。單純指定數據持久層的動做不會將其中全部的數據放到上下 文中去。 當你想從數據存儲層中獲取某些對象的時候,你只會獲得那些你請求的(有點羅嗦,總的意思就是須要時獲取,獲取的就是須要的)。若是你不在須要這個對象的時 候,默認狀況下它會被釋放。(固然,只是釋放這個對象,而不是從對象圖中移除該對象)。——注:我的感受有點像從新拷了一個文件的某些部分,不用了就在副 本中刪除,不會影響原件。
持久化存儲助理(Persistent Store Coordinator)
以前提到過,程序中的對 象和外部存儲的數據經過Core Data框架中的一系列對象進行協調,這一系列的對象總的被稱爲持久存儲棧(Persistence stack)。在棧頂是被管理對象上下文(Managed object context),而棧底是持久化對象存儲層(Persistence object store)。在它們之間就是持久化存儲助理。
事實上,持久化存儲助理定義了一個棧。從設計方面考慮,它就是能夠做爲上下 文的」外觀「, 這樣多個數據存儲(Persistence store)看起來就像是一個。 而後上下文就能夠根據這些數據存儲來建立對象圖了。持久化存儲助理智能關聯一個被管理對象的模型。若是你像要把不一樣的實體放到不一樣的存儲中去,就須要爲你 的模型實體作「分區」,方式是經過定義被管理對象模型的configurations。(請參考"Configurations"一章)。
Figure 4演示了這樣的一個結構:employees和departments存儲在一個文件中,customers和companies存儲在另一個文件中。當你要獲取對象的時候,它們從相關的文件中自動獲取;當保存時,又被歸檔到相應的文件中。
Figure 4存儲棧—改
持久化存儲(Persistent Stores)
持久化存儲是和單獨的一個文件或外部的數據關聯的,它負責將數據和上下文中的對象進行對應。一般,須要你直接和持久化對象存儲打交道的地方,就是指定新 的、 和程序進行關聯的外部數據的位置(例如,當用戶打開或保存一個文檔)。大多數須要訪問持久化存儲的動做都由上下文來完成。
程序的代碼—— 特別是和被管理對象相關的部分——不該該對持久化存儲作任何假設(也就是不須要本身考慮存儲的方式或過程)。 Core Data對幾種文件格式有原生的支持。你能夠選擇一種本身程序須要的。假設在某個階段你決定換一種文件的格式,而又不想修改程序的框架,並且,你的程序作 了適當的抽象(注:這個就屬於設計方面的東東了),這時,你就能嚐到使用Core Data的甜頭了。例如,在最初的設計中,程序只從本地文件中獲取數據,而你的程序沒有去硬指定對應數據的獲取位置,而是能夠在後期指定從遠程位置添加新 的數據類型,這樣你就可使用新的類型,而不須要修改代碼。(這段仍是感受翻的不太合適)。
重要提示:
雖然Core Dta支持SQLite做爲一種存儲類型,但它不能使用任意的SQLite數據庫。Core Data在使用的過程種本身建立這個數據庫。(詳情,請參考"Persistence Store Features")。
持久化文檔(Persistent Documents)
你能夠經過代碼的方式建立和配置持久存儲棧,但在多數狀況下,你只是想建立一個基於文檔 的應用程序(Document-based application,這個是mac上的)來讀寫文件。這時,用NSDocument的子類NSPersistentDocument可讓你感覺到使 用Core Data的便利。默認情況下,NSPersistentDocument就已經建立了它本身的持久存儲棧,其中包含了上下文,和單個的持久對象存儲,來處 理這樣文檔和外部數據「一對一」的映射關係。
NSPersistentDocument類提供了訪問文檔的上下文的方法,也實現了標準的NSDocument方法來經過Core Data讀寫文件。 通常說來,你不須要編寫額外的代碼來處理對象的持久化。
持久化文檔的撤銷(undo)操做也被集成在被管理對象的上下文中。
被管理對象和被管理對象模型(Managed Objects and the Managed Object Model)
爲 了管理對象圖,也爲了提供對象持久化的功能,Core Data須要對對象有很強的描述能力。被管理對象模型就是程序中對象、實體描述的概要圖,如圖Figure 5所示。建立模型的經常使用作法是經過Xcode的圖形化建模工具Date Model Design tool。可是若是你願意的話,也能夠在運行時經過代碼來建模。
Figure 5 有兩個實體的對象模型
Figure 6 帶有兩個屬性和一個關係的的實體描述
被管理對象模型(Managed Object Models)
多數Core Data的功能依賴於你建立的,用來描述程序的實體及其屬性、關係的模型圖。 模型圖由NSManagedObjectModel所表示。通常說來,模型的信息越充實,Core Data能提供的功能就越好。 下文講解了對象模型的特性,以及如何在程序中建立、使用對象模型。
被管理對象模型的特性
被管理對象模型是 NSManagedObjectModel的實例。它描述了你在程序中使用的實體的概要信息。(若是讀者不瞭解entity、property、 attribute和relationship的含義,請先查看"Core Data Basics"和"Cocoa Design Patterns"文檔中的"Object Modeling"一節)
實體(Entities)
模型包含了NSEntityDescription對象,NSEntityDescription對象指代了模型的實體。關於實體由兩個重要特徵:名稱 (name)和類名(name of class)。你應該弄清楚實體、實體的類和做爲實體實例的被管理對象之間的區別。
NSEntityDescription 對象可包含NSAttributeDescription對象(指代實體的attribute)和NSRelationshipDescription對 象(指代實體間的relationship)。實體也可能包含fetched屬性,該屬性由NSFetchedPropertyDescription指 代,模型中有對應的fetch請求的模板,fetch請求由NSFetchRequest所指代。
實體的繼承關係
實體的繼承和類 的繼承很相似,固然,也一樣有用。 若是你有若干個類似的實體,就能夠抽離出它們的共有特性做爲一個「父實體」,就省去了在多個實體中都指定相同的屬性。 例如,你能夠定義一個包含firstName和lastName的「Person」實體,而後在定義子實體"Employee"和"Customer"。
若是是使用Xcode的可視化建模工具來建立模型,你就能夠經過以下圖的方式爲一個實體指定父級實體。
Figure1 Xcode中爲一個實體指定父實體
抽象實體
你能夠把一個實體指定爲「抽象實體」,也就是說,你不打算使用這個實體來建立實例。一般,當你想把這個實體做爲父實體,而有子實體來實現詳細內容的時候, 就 把它聲明「抽象實體」。(和抽象類很像)。例如,在一個繪圖程序中,你可能會設計一個Graphic實體,它包含了x和y座標信息、顏色、繪製區域,而你 不會去建立一個Graphic的實例,而是使用具體的子實體——Circle、TextArea、Line。(這些基本的東西就不給大牛們再羅嗦 了。。。)
Properties(屬性,這個和Attributes的意思同樣,實在區別不出來,只好上英語了)
實體的 Properties是它的attributes和relationship,包含了fetched屬性(若是有的話)。每一個property都有名稱和 類型。 Attribute也可能有默認值。property的名稱不能和NSObject和NSManagedObject類中的無參方法名相同。例如,不能把 property命名爲"description"。
臨時屬性(Transient Property)也是做爲模型的一部分,可是不做爲實體實例的數據保存在持久存儲層。 Core Data也會跟蹤臨時屬性的變化,以備撤銷操做時使用。
注意:若是你用模型外的信息對臨時屬性執行撤銷操做,Core Data將不會使用舊值,調用你的set方法——它只會更新快照信息(snapshot information)。(這段怪怪的,用到的話在修改一下翻譯吧)
Attributes
Core Data內部支持各類attribute的類型,例如string,date,integer(NSString, NSDate, NSNumber)。若是你使用那些不支持的數據,你須要用到在「Non-Standard Persistent Attributes」介紹到的技術。
你能夠將一個attribute聲明爲「可選」(optional),可選的attribute不 必須有值,可是,不鼓勵你將屬性置空——尤爲是數字值(更好的解決方案是使用強制的值,在這裏,咱們用默認值,例如0)。 這樣作的緣由是爲了配合SQL中對於空值NULL作比較的操做:NULL不一樣於Objective-C中的nil。 數據庫中的NULL不一樣於0,搜索0值的操做不會匹配到值爲NULL的列。
false == (NULL == 0)
false == (NULL != 0)
並且,在數據庫中,NULL也不等於空字符串或是空的數據對象:
false == (NULL == @"")
false == (NULL != @"")
它們之間一點關係都沒有。
關係(Relationships)
Core Data支持對1、對多的關係,也支持fetched屬性。 Fetched property表示了一種「弱」的、單項的關係。 在employees和departments的例子中, department 的一個fetched property多是「最近僱傭人」(recent hires),而反過來,employee不會擁有這樣的關係。
獲取數據請求的模板(Fetch Request Templates)
咱們使用NSFetchRequest類來描述數據請求,利用數據請求從持久存儲(persistent store)中獲取對象。 常常須要屢次執行一樣的請求,或是執行某種模式的請求,可是其中包含可變的元素(如查找條件)——這些元素常常有用戶提供。 例如,在運行的時候,你要根據用戶須要獲取某個做者在某個指定日期後的出版的全部出版物。
你能夠預約義請求,把它們做爲模板存儲在被管理對象模型中。 預約義的模板在你須要的時候就能夠取出使用。一般狀況下,咱們經過Xcode的data modeling tool工具建立請求模板。模板能夠包含變量,如圖Figure 2所示。
Figure 2 Xcode predicate builder
關於Fetch request templates的詳細信息,請查看"Accessing and Using a Managed Object Model at Runtime"的描述。
用戶信息字典(User Info Dictionaries)
模型中的許多元素,諸如entities, attributes, relationships,都有相關的用戶信息字典。用熟悉的鍵-值對,你能夠向其中放置任何你須要的數據。這裏經常使用的信息有實體的版本詳情,還有針對 fetched property,給謂詞(predicate)用的值。
配置(Configurations)
配置包含了一個名稱和若干個相關的實體。實體的集合是能夠重疊的——這就是說,一個實體能夠出如今多個配置中。在代碼中,咱們使用 setEntities: forConfiguration:的方法來指定配置。也能夠用Xcode的建模工具來指定(選中某個實體,就在屬性窗口的第三個,就是一個小扳手的符 號)。要獲取某項配置的實體,須要用entitiesForConfiguration:的方法。
通常說來,若是你想把不一樣的實體存放在不一樣的存儲中去,就可能用到配置。一個持久化存儲助理(persistent store coordinator)只能有一個被管理對象模型。因此,默認狀況下,和助理關聯的某個存儲必須包含一樣的實體。要想繞過這個限制,你能夠建立一個包含 實體子集的模型,而後爲每個子集建立配置,這樣一來,使用這個模型建立助理,當你須要添加存儲時,可以使用不一樣的配置指定對應的存儲屬性。當你建立配置的 時候,須要記住,不能建立跨存儲的關係。
使用被管理對象模型
一般可使用Xcode的建模工具來建立模型(請參考"Create a managed object with Xcode")。你也能夠所有使用代碼來建立(請參考"Core Data Utility Tutorial")。
編譯數據模型
數據模型是一種部署資源。 在模型中,除了有實體和屬性的詳細信息外,用Xcode建立的模型還包含了一些額外的視圖信息,包括佈局、顏色等等。這些信息在運行時不是必須的。模型文 件在編譯的過程當中會刪除這些額外信息以保證儘量高效的加載。xcdatamodel「源」文件會被momc編譯器編譯爲mom的目標文件。
"mom" 位於 /Library/Application Support/Apple/Developer Tools/Plug-ins/XDCoreDataModel.xdplugin/Contents/Resources/,若是你想把它用在本身的 build腳本中,格式是:mom source destination, source 就是Core Data Model文件,destination就是輸出的mom文件。
加載數據模型
在一些狀況下,你不須要寫任何加載模型的代碼。若是你使用基於文檔的程序框架(Document-based application),NSPersistentDocument會管理諸如查找模型、加載模型的任務。 若是你建立了非Document-based application,並且裏面又用到了Core Data,通常將獲取模型的代碼放在application delegate裏。模型的存儲名稱——也就是文件名,
和運行時的名稱是不相關的,一旦模型被加載,文件名就沒有什麼意義了。也就是說,對模型文件,你能夠隨意命名。
若是你想手動加載模型,有兩種方式可用,它們各有各的好處:
你能夠從指定的bundle集合裏建立整合模型,使用以下的類方法:
mergeModelFromBundles:
也能夠用指定的URL加載單個的模型,使用以下的實例方法:
initWithContentsOfURL: (這個方法相信你們都用過)
若不須要考慮分開加載模型,第一個類方法很適用。例如:在你的程序中和程序連接的framework裏都有你想要加載的模型。這個類方法可讓你很輕鬆的加載全部的模型,而不須要考慮模型文件的名稱,也不用特定的初始化方法來保證全部的模型都被找到。
可是當你有多個模型要加載,特別是這些模型都表明了一個schema的不一樣版本,這時,知道要加載哪一個模型就很重要了(合併包含相同實體的模型可能致使命 名衝突和錯誤,咱們以前「一鍋端」的方法不太合適了)。在這種狀況下,咱們能夠用第二個實例方法。 另外,有時咱們也須要將模型存儲在bundle以外,也須要用這個方法從指定的URL位置加載模型。
還有一點須要說明:咱們還有一個類方法 modelByMergingModels:能夠用。像mergedModelFromBundles:方法同樣,它也能合併給定的若干個模型。這樣,我 們就能夠經過URL來逐一加載模型,而後在建立助理對象以前將它們整合爲一個。
改變模型
因爲模型描述了存儲層數據的結構,任何改變模型的動做都將使其不在適配於以前建立的存儲層。 若是你改變了模型的結構,就須要將當前存儲層的數據遷移到新版本。(請參考"Core Data Model Versioning and Data Migration Programming Guide"文檔)。例如:若是你添加了新的實體,或新的屬性,你將沒法打開舊的存儲;若是你添加了驗證的限制,或者爲屬性添加了新的缺省值,你就能夠打 開舊的存儲。
在運行時訪問和適用被管理對象模型
在運行時,被管理對象模型就是一個簡單的「對象圖」(這個概念以前提到過),認識到這點很重要,尤爲是當你須要用代碼來訪問模型的詳細信息時。例如:修改 模型(你只能在runtime以前這樣作,請參考 NSManagedObjectModel),取回信息(如本地化實體名,屬性數據類型,或數據請求模板)。
在運行時訪問模型有不少方法,經過持久棧最終從持久化存儲助理獲得模型,代碼以下:
[[aManagedObjectContext persistentStoreCoordinator]managedObjectModel];
你也能夠經過實體描述獲得模型,所以給定一個被管理對象,你就能夠獲得它的實體描述,進而得到模型。代碼以下:
[[aManagedObject entity] managedObjectModel];
某些狀況下,你要維護模型的「直接」引用,也就是說,一個直接返回模型的方法。NSPersistentDocument提供了 managedObjectModel方法,能夠返回一個模型,該模型和在文檔的上下文中使用的持久化存儲助理相關聯。若是你使用Core Data Appplication的模板,application delegate將負責模型的引用。
經過代碼建立獲取數據請求模板(Fetch Request Templates)
你能夠經過代碼建立數據請求模板並將其和模型關聯,方法是:setFetchRequestTemplate: forName:如Listing-1所示。 提醒一下:你只能在模型被助理(coordinator)使用以前修改它。
Listing 1 經過代碼建立獲取數據請求模板
NSManagedObjectModel *model = …;
NSFetchRequest * requestTemplate = [[NSFetchRequest alloc]init];
NSEntityDescription *publicationEntity =
[[model entitiesByName] objectForKey: @"Publication"];
[requestTemplate setEntity: publicationEntity];
NSPredicate *predicateTemplate = [NSPredicate predicateWithFormat:
@"(mainAuthor.firstName like[cd] $FIRST_NAME) AND \
(mainAuthor.lastName like[cd] $LAST_NAME) AND \
(publicationDate > $DATE)"];
[requestTemplate setPredicate: predicateTemplate];
[model setFetchRequestTemplate: requestTemplate
forName: @"PublicationForAuthorSinceDate"];
[requestTemplate release];
訪問請求模板
你能夠用"Accessing and Using a Managed Object Model at Runtime"裏介紹的代碼片斷來獲取並使用請求模板。替換字典必須包含和模板中定義的變量對應的鍵。若是你想測試null值,必須使用NSNull對 象——參考"Using Predicates"。(注:這裏的替換字典很好理解,以前的模板中用到了諸如$FIRST_NAME, $LAST_NAME, $DATE這些東西,就至關於咱們在模板中建立好的「變量」,咱們須要把一個模板「具體化」,就用替換字典,將裏面的變量對應一個值,這裏看代碼就明白 了。)
NSManagedObjectModel *model = …;
NSDictionary *substitutionDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
@"Fiona", @"FIRST_NAME", @"Verde", @"LAST_NAME",
[NSDate dateWithTimeIntervalSinceNow: -31356000], @"DATE", nil]; //這裏的FIRST_NAME, LAST_NAME, DATE和咱們以前模板裏的$FIRST_NAME, $LAST_NAME和$DATE對應
NSFetchRequest *fetchRequest =
[model fetchRequestFromTemplateWithName: @"PublicationForAuthorSinceDate"
substitutionVariables: substitutionDictionary]; //從以前的model中拿出請求模板,而後設定替換字典
NSArray *results =
[aManagedObjectContext executeFetchRequest: fetchRequest error: &error];
要是模板裏不包含可替換的變量,你要麼
1. 使用fetchRequestFromTemplateWithName: substitutionVariables: 方法,傳遞nil給第二個參數
或者:
2. 使用fetchRequestTemplateForName: 並將結果copy。這個方法不須要傳遞「替換變量」這個參數,可是若是你要用返回值自己,將會有異常拋出(沒法在不可變的模型中修改命名的數據請 求"Can't modify named fetch request in an immutable model")。
本地化被管理對象模型
你能夠對模型的大部份內容作本地化處理,包括實體和屬性名,還有錯誤信息。要明白,「轉成你本身的語言」也是本地化的一部分。 即便你不打算提供外語版本, 顯示「天然語言」的出錯提示信息也會有更好的用戶體驗。例如:「First Name is a required property」就比"firstName is a required property"更好。(後面的這個更像是開發者用的log,顯示的是變量名,這裏不太明顯)。
要想對模型進行本地化處理,須要提供一個本地化字典,模式以下:
Table 1 針對被管理對象模型的本地化字典鍵值對應關係:
備註:(1)在不一樣實體中的屬性,擁有相同的原始名稱,但須要不一樣的本地化名稱,適用於該格式。
咱們能夠經過localizationDictionary方法來訪問本地化字典。注意:在Mac OS X 10.4上,這個方法可能返回nil,除了Core Data爲了某些特定目的(如報告本地化的錯誤描述)延遲加載本地化字典。
字符串文件
處理模型的本地化最簡單的方法就是建立對應的字符串文件——字符串文件名和模型文件名一直,可是後綴名用.strings。(例如,模型文件名爲 MyDocument.xcdatamodel,對應的字符串文件名就爲MyDocumentModel.strings;若是模型文件已經包含了 Model後綴,你必須再附加一個Model,因此,若是模型文件名爲JimsModel.xcdatamodel對應的字符串文件名爲 JimsModelModel.strings)。字符串文件格式和標準字符串文件相似(請參考"Localizing String Resources"),可是對應的鍵值要遵循Table-1中的規則。
一個模型的字符串文件實例:
更詳細的示例請參考"NSPersistentDocument Core Data Tutorial"。
代碼實現設置本地化字典
你能夠在運行時設定本地化字典,適用NSManagedObjectModel的setLocalizationDictionary:方法便可。你必須 建立一個符合Table-1格式的字典,並把它和模型關聯。必須保證在模型被使用(獲取或建立被管理對象)以前作這些工做,由於再使用後模型就不可編輯 了。 Listing 3演示了建立包含本地化字典的被管理對象模型。實體名稱叫「Run」,它有兩個屬性: "date"和"processID",分別是date和integer類型。process ID的值不能爲負。
Listing 3 經過代碼建立被管理對象模型
這段代碼寫的比較多,這裏再也不解釋了。本地化字典的代碼在最後。建立一個符合格式的localizationDictionary,而後用model調用便可。
數據持久化總結
1 //1.沙盒:/Users/nono/Library/Application Support/iPhone Simulator/5.1/Applications/2D135859-1E80-4754-B36D-34A53C521DE3 2 /** 3 // 一、獲取程序的Home目錄 4 NSString *home = NSHomeDirectory(); 5 NSLog(@"應用程序目錄:%@", home); 6 7 // 二、獲取Documents目錄 8 // NSUserDomainMask 表明從用戶文件夾下找 9 // YES 表明展開路徑中的波浪字符「~」 10 NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 11 // 只有一個匹配目錄,因此這個集合裏面只有一個元素 12 NSString *doc = documents[0]; 13 NSLog(@"文檔目錄:%@", doc); 14 15 // 使用字符串拼接的方式獲取目錄名 16 // 不建議採用,由於新版本的操做系統可能會修改目錄名 17 NSString *doc2 = [home stringByAppendingPathComponent:@"Documents"]; 18 NSLog(@"拼接文檔目錄:%@", doc2); 19 20 // 三、獲取Cache目錄 21 NSArray *caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 22 NSString *cache = caches[0]; 23 NSLog(@"緩存目錄:%@", cache); 24 25 // 四、獲取Tmp目錄 26 NSString *tmpDir = NSTemporaryDirectory(); 27 NSLog(@"臨時目錄:%@", tmpDir); 28 29 //5,NSUserDefaults 30 在A類中: 31 NSUserDefaults * userDefault = [NSUserDefaultsstandardUserDefaults]; 32 [userDefault setBool:YES forKey:@"isonline"]; 33 [userDefault setInteger:111 forKey:@"online_user_number"]; 34 ...等等。參見NSUserDefault用法。 35 36 在B中:獲取A傳遞過來的參數 37 NSUserDefault * userDefault = [NSUserDefault standardUserDefault]; 38 BOOL isonline = [userDefault boolForKey:@"isonline"]; 39 NSInteger onlineUserNumber = [userDefault integerForKey:@"online_user_number"]; 40 41 */ 42 43 //2,屬性列表 44 /** 45 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 46 NSString *docPath = [paths objectAtIndex:0]; 47 NSString *myFile = [docPath stringByAppendingPathComponent:@"my.list"]; 48 //讀取文件 49 NSArray *array = [[NSArray alloc] initWithContentsOfFile:myFile]; 50 //操做完若修改了數據則,寫入文件 51 [array writeToFile:myFile atomically:YES]; 52 */ 53 54 55 //3.對象歸檔 56 /** 57 #pragma NSCoding協議實現實現 58 - (void)encodeWithCoder:(NSCoder *)aCoder 59 { //encoder 60 [aCoder encodeObject:stringAforKey:@"1"]; 61 [aCoder encodeObject:stringBforKey:@"2"]; 62 } 63 - (id)initWithCoder:(NSCoder *)aDecoder 64 { 65 //decoder 66 if (self = [superinit]) { 67 stringA = [[aDecoder decodeObjectForKey:@"1"] retain]; 68 stringB = [[aDecoder decodeObjectForKey:@"2"] retain]; 69 } 70 returnself; 71 } 72 73 #pragma NSCopying協議實現 74 - (id)copyWithZone:(NSZone *)zone 75 { 76 TestObj *copy = [[[selfclass] allocWithZone:zone] init]; 77 copy.stringA = [[self.stringAcopyWithZone:zone] autorelease]; 78 copy.stringB = [[self.stringBcopyWithZone:zone] autorelease]; 79 return copy; 80 } 81 82 //讀取歸檔文件 83 NSData *data = [[NSMutableDataalloc] initWithContentsOfFile:myFile]; 84 NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiveralloc] initForReadingWithData:data]; 85 TestObj * test = [unarchiver decodeObjectForKey:@"data"]; 86 [unarchiver finishDecoding]; 87 [data release]; 88 [unarchiver release]; 89 90 //寫入歸檔文件 91 NSMutableData *data1 = [[NSMutableDataalloc] init]; 92 NSKeyedArchiver *archiver = [[NSKeyedArchiveralloc] initForWritingWithMutableData:data1]; 93 [archiver encodeObject:test forKey:@"data"]; 94 [archiver finishEncoding]; 95 [data writeToFile:myFile atomically:YES]; 96 [data1 release]; 97 [archiver release]; 98 [test release]; 99 */ 100 101 102 //4.數據庫存儲(SQLite3) 103 /** 104 //數據庫操做 105 sqlite3 *database; 106 // const NSString * dbname = @"mydb" 107 int result; 108 //打開一個指定路徑的現有的數據庫,若是沒有則會新建一個db庫 109 result = sqlite3_open([myFile UTF8String], &database); 110 if (result != SQLITE_OK) { 111 sqlite3_close(database); 112 } 113 114 //建立一個db表 115 char *errorMsg; 116 NSString *sql_create_table = @"CREATE TABLE IF NOT EXISTS NONOTABLE 省略~~~~~~~~~~~~~"; 117 int result1 ; 118 //sqlite_exec用了針對sqlite3運行任何不要返回數據的命令,它用於執行更新,插入和刪除。簡單來講,這個方法執行的都是一些無需返回數據(雖然咱們可能獲取一個狀態值。)。 119 result1 = sqlite3_exec(database, [sql_create_table UTF8String], NULL, NULL, &errorMsg); 120 121 //檢索查詢操做 122 int result2 ; 123 sqlite3_stmt *statment; 124 NSString *sql_selected = @"查詢語句"; 125 result2 = sqlite3_prepare_v2(database, [sql_selected UTF8String], -1, &statment, nil); 126 if(result2 == SQLITE_OK){ 127 //單步操做 128 while (sqlite3_step(statment) == SQLITE_ROW) { 129 int row = sqlite3_column_int(statment, 0); 130 char * rpwData = sqlite3_column_text(statment, 1); 131 } 132 sqlite3_finalize(statment); 133 } 134 135 136 //綁定變量,既就是插入操做的一種變種,好比我麼那上面提到sqlite_exec能夠執行插入操做,插入內容直接是寫在sql字竄裏,可是考慮到字竄涉及到無效的符號以及會一些嚴重的注入漏洞(好比之前聽過的引號符號)。 137 NSString *sql_bind = @"insert into foo value(?,?)"; 138 result2 = sqlite3_prepare_v2(database, [sql_selected UTF8String], -1, &statment, nil); 139 if(result2 == SQLITE_OK){ 140 sqlite3_bind_int(statment, 1, 235); 141 sqlite3_bind_text(statment, 2, "test", -1, nil); 142 sqlite3_finalize(statment); 143 } 144 if (sqlite3_step(statment) != SQLITE_DONE) 145 NSLog(@"error"); 146 sqlite3_finalize(statment); 147 148 149 sqlite3_close(database); 150 151 */ 152 153 154 //5.蘋果公司提供的持久性工具Core Data 155 /* 156 通常須要定義如下Core Data的三個必備 157 NSPersistentStoreCoordinator *persistentStoreCoordinator; 158 NSManagedObjectModel *managedObjectModel; 159 NSManagedObjectContext *managedObjectContext; 160 以及使用時須要用到de 161 NSFetchedResultsController *fetchedResultsController; 162 */ 163 164 165 166 167 168 169 //其實對於ios數據存儲,最經常使用和主要要掌握的就是屬性列表和數據庫,由於兩個是出鏡率比較高的。其餘可能在數據存明顯體現出儲優點時,咱們會去考慮用另外兩種機制。基礎的來講,必須掌握屬性列表和sqlite的操做存儲。