iOS數據本地持久化

 

p1:歸檔、Preference(NSUserDefault)、沙盒存儲

iOS開發中本地存儲主要有三種形式

  • XML屬性列表(plist)歸檔
  • Preference(偏好設置)
  • NSKeyedArchiver歸檔(NSCoding)

應用沙盒

什麼是應用沙盒

要想在本地存儲數據,那就要知道一下什麼是應用沙盒 ,其實很好理解應用沙盒就是應用的文件夾,與其餘文件系統隔離。每個iOS應用都有本身的應用沙盒,應用必須待在本身的沙盒裏,其它應用不能訪問該沙盒。
如何獲取應用沙盒路徑,能夠經過打印NSHomeDirectory()來獲取應用沙盒路徑,下圖爲打印路徑結果:html


屏幕快照 2015-12-03 22.10.07.png


Melody_Zhy 是用戶文件夾(樣子是個小房子)
3CEC8EEB-C230-44BE-93B7-DF3B9A120A94 iOS8以後每次運行Xcode都會生成不一樣的沙盒路徑,不一樣之處就是最後這個文件夾名,多是蘋果爲了安全着想android


應用沙盒結構分析

首先咱們先來看下,應用沙盒裏面都有什麼ios


屏幕快照 2015-12-03 22.27.50.png


這裏提一下Finder的快捷鍵 shift + com + g 能夠前往任意路徑的文件夾,所以咱們能夠打印沙盒路徑以後將沙盒路徑複製到Finder前往路徑文件夾中,前往應用沙盒。這是一個比較耽誤事的方法!幸虧有一款叫作simpholders的app,它能夠很簡單的訪問應用的沙盒路徑,記得去下載simpholders2哦,第一代iOS8以後就不能用了,app很簡單易懂,用下就會了~
如今咱們來看看應用沙盒裏面這些文件夾都是作什麼用的git

  • Documents :保存應用運行時生成的須要持久化的數據,iTunes同步設備時會備份該目錄。例如,遊戲應用可將遊戲存檔保存在該目錄
  • Library/Caches : 保存應用運行時生成的須要持久化的數據,iTunes同步設備時不會備份該目錄。通常存儲體積大、不須要備份的非重要數據 
  • Library/Preference : 保存應用的全部偏好設置,iOS的Settings(設置)應用會在該目錄中查找應用的設置信息。iTunes同步設備時會備份該目錄
  • tmp : 保存應用運行時所需的臨時數據,使用完畢後再將相應的文件從該目錄刪除。應用沒有運行時,系統也可能會清除該目錄下的文件。iTunes同步設備時不會備份該目錄

應用沙盒目錄的常見獲取方式

沙盒根目錄的獲取方式

正如上面咱們所說:github

NSString *home = NSHomeDirectory();
Documents文件夾的獲取方式(3種)

第一種( !笨!)sql

// 利用沙盒根目錄拼接字符串 NSString *homePath = NSHomeDirectory(); NSString *docPath = [homePath stringByAppendingString:@"/Documents"];

第二種( !還👌!)數據庫

// 利用沙盒根目錄拼接」Documents」字符串 NSString *homePath = NSHomeDirectory(); NSString *docPath = [homePath stringByAppendingPathComponent:@"Documents"];

可是不建議使用這種方法,由於不定哪天蘋果大大就把文件名稱改了呢-_-!編程

第三種( !~推薦~ !)數組

// NSDocumentDirectory 要查找的文件 // NSUserDomainMask 表明從用戶文件夾下找 // 在iOS中,只有一個目錄跟傳入的參數匹配,因此這個集合裏面只有一個元素 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [path stringByAppendingPathComponent:@"xxx.plist"];

這裏我來詳細的說下NSSearchPathForDirectoriesInDomains這個方法的幾個參數 :
<#NSSearchPathDirectory directory#> 這個參數表明要查找的文件,是個枚舉! 枚舉你懂的點擊去看看就知道了~
<#NSSearchPathDomainMask domainMask#> 這個參數表明從用戶文件夾下找,也是枚舉!
最後一個參數若是是NO的話,打印的路徑會是這種形式~/Documents,咱們通常都會用YES,這樣能夠獲取完整路徑字符串!
這個方法的返回值是一個數組,但在iOS中,只有一個目錄跟傳入的參數匹配,因此這個集合裏面只有一個元素,因此咱們取第一個元素!緩存


Library/Caches文件夾的獲取方式(跟上面的方法類似)

這裏我只用上面的第三種方法!注意第一個參數!

NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [path stringByAppendingPathComponent:@"student.data"];

tmp文件夾的獲取方式
NSString *tmp= NSTemporaryDirectory();

Library/Preference文件夾的獲取方式

經過NSUserDefaults類存取該目錄下的設置信息!
!!!這個下面會有介紹!!!

XML屬性列表(plist)歸檔

plist文件

plist的根Type只能是字典(NSDictionary)或者是數組(NSArray)因此歸檔時咱們只能將數組或字典保存到plist文件中,可是NSString也能經過歸檔保存到plist文件中同時它也能夠經過stringWithContentsOfFile解檔,它保存到plist中時Type是空的,Value是有值的!


plist文件的歸檔
NSArray *arr = [[NSArray alloc] initWithObjects:@"1", @"2", nil]; // NSDocumentDirectory 要查找的文件 // NSUserDomainMask 表明從用戶文件夾下找 // 在iOS中,只有一個目錄跟傳入的參數匹配,因此這個集合裏面只有一個元素 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [path stringByAppendingPathComponent:@"xxx.plist"]; [arr writeToFile:filePath atomically:YES];
plist文件的解檔
NSString *filePath = [path stringByAppendingPathComponent:@"xxx.plist"]; // 解檔 NSArray *arr = [NSArray arrayWithContentsOfFile:filePath]; NSLog(@"%@", arr);

Preference(偏好設置)

OC中有一個NSUserDefaults的單例,它能夠用來存儲用戶的偏好設置,例如:用戶名,字體的大小,用戶的一些設置等,下面我用兩個UISwitch來演示如何保存用戶設置開關的關閉狀態


保存用戶偏好設置
// 獲取用戶偏好設置對象 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // 保存用戶偏好設置 [defaults setBool:self.one.isOn forKey:@"one"]; [defaults setBool:self.two.isOn forKey:@"two"]; // 注意:UserDefaults設置數據時,不是當即寫入,而是根據時間戳定時地把緩存中的數據寫入本地磁盤。因此調用了set方法以後數據有可能尚未寫入磁盤應用程序就終止了。 // 出現以上問題,能夠經過調用synchornize方法強制寫入 // 如今這個版本不用寫也會立刻寫入 不過以前的版本不會 [defaults synchronize];
讀取用戶偏好設置
// 讀取用戶偏好設置 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; self.one.on = [defaults boolForKey:@"one"]; self.two.on = [defaults boolForKey:@"two"];

NSKeyedArchiver歸檔(NSCoding)

只有遵照了NSCoding協議的類才能夠用NSKeyedArchiver歸檔和NSKeyedUnarchiver解檔,若是對象是NSString、NSDictionary、NSArray、NSData、NSNumber等類型,能夠直接用NSKeyedArchiver歸檔和NSKeyedUnarchiver解檔~
下面我舉的🌰是歸檔解檔一個Student模型,所以該模型應該遵照NSCoding協議


實現encodeWithCoder和initWithCoder方法
- (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:self.name forKey:@"name"]; [coder encodeInteger:self.age forKey:@"age"]; } - (instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { self.age = [coder decodeIntegerForKey:@"age"]; self.name = [coder decodeObjectForKey:@"name"]; } return self; }
歸檔
Student *s1 = [[Student alloc] init];
s1.name = @"zzz"; s1.age = 18; NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; // 這個文件後綴能夠是任意的,只要不與經常使用文件的後綴重複便可,我喜歡用data NSString *filePath = [path stringByAppendingPathComponent:@"student.data"]; // 歸檔 [NSKeyedArchiver archiveRootObject:s1 toFile:filePath];
解檔
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [path stringByAppendingPathComponent:@"student.data"]; // 解檔 Student *s = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; NSLog(@"%@----%ld", s.name, s.age);

相關連接:iOS開發中本地數據存儲的總結

 

以上的全部存儲方法,都是覆蓋存儲。若是想要增長一條數據就必須把整個文件讀出來,而後修改數據後再把整個內容覆蓋寫入文件。因此它們都不適合存儲大量的內容,大量存儲須要用SQLite、CoreData等!

p2:KeyChain

ios的機密數據用KeyChain保存

相關連接:ios KeyChain中保存數據

              ios KeyChain項目中應用到的內容

 

 

p3:ASIHTTPRequest本地緩存

相關連接:使用ASIHTTPRequest和ASIDownloadCache實現本地緩存

一、設置全局的Cache
    在AppDelegate.h中添加一個全局變量

@interface AppDelegate : UIResponder   
{  
    ASIDownloadCache *myCache;  
}  
@property (strong, nonatomic) UIWindow *window;  
@property (nonatomic,retain) ASIDownloadCache *myCache;
   在AppDelegate.m中的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中添加以下代碼
//自定義緩存  
ASIDownloadCache *cache = [[ASIDownloadCache alloc] init];  
self.myCache = cache;  
[cache release];  
//設置緩存路徑  
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
NSString *documentDirectory = [paths objectAtIndex:0];  
[self.myCache setStoragePath:[documentDirectory stringByAppendingPathComponent:@"resource"]];  
[self.myCache setDefaultCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy];

     在AppDelegate.m中的dealloc方法中添加以下語句  

 [myCache release];
    到這裏爲止,就完成了全局變量的聲明。

 

    二、設置緩存策略

    在實現ASIHTTPRequest請求的地方設置request的存儲方式,代碼以下

NSString *str = @"http://....../getPictureNews.aspx";  

NSURL *url = [NSURL URLWithString:str];  

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];  

//獲取全局變量  

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];  

//設置緩存方式  
[request setDownloadCache:appDelegate.myCache];  
//設置緩存數據存儲策略,這裏採起的是若是無更新或沒法聯網就讀取緩存數據  
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];  
request.delegate = self;  
[request startAsynchronous];
    三、清理緩存數據

 

    我在這裏採用的是手動清理數據的方式,在適當的地方添加以下代碼,我將清理緩存放在了應用的設置模塊:

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];  
[appDelegate.myCache clearCachedResponsesForStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; 

     這裏清理的是ASICachePermanentlyCacheStoragePolicy這種存儲策略的緩存數據,若是更換其餘的參數的話,便可清理對應存儲策略的緩存數據。

 

p4:SQLite/FMDB

SQLite3

以前的全部存儲方法,都是覆蓋存儲。若是想要增長一條數據就必須把整個文件讀出來,而後修改數據後再把整個內容覆蓋寫入文件。因此它們都不適合存儲大量的內容。

1.字段類型

表面上SQLite將數據分爲如下幾種類型:

  • integer : 整數

  • real : 實數(浮點數)

  • text : 文本字符串

  • blob : 二進制數據,好比文件,圖片之類的

實際上SQLite是無類型的。即無論你在創表時指定的字段類型是什麼,存儲是依然能夠存儲任意類型的數據。並且在創表時也能夠不指定字段類型。SQLite之因此什麼類型就是爲了良好的編程規範和方便開發人員交流,因此平時在使用時最好設置正確的字段類型!主鍵必須設置成integer

2. 準備工做

準備工做就是導入依賴庫啦,在iOS中要使用SQLite3,須要添加庫文件:libsqlite3.dylib並導入主頭文件,這是一個C語言的庫,因此直接使用SQLite3仍是比較麻煩的。

3.使用

  • 建立數據庫並打開

操做數據庫以前必須先指定數據庫文件和要操做的表,因此使用SQLite3,首先要打開數據庫文件,而後指定或建立一張表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
*  打開數據庫並建立一個表
*/
- (void)openDatabase {
    //1.設置文件名
    NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@ "person.db" ];
    //2.打開數據庫文件,若是沒有會自動建立一個文件
    NSInteger result = sqlite3_open(filename.UTF8String, &_sqlite3);
    if  (result == SQLITE_OK) {
        NSLog(@ "打開數據庫成功!" );
        //3.建立一個數據庫表
        char *errmsg = NULL;
        sqlite3_exec(_sqlite3,  "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)" , NULL, NULL, &errmsg);
        if  (errmsg) {
            NSLog(@ "錯誤:%s" , errmsg);
        else  {
            NSLog(@ "創表成功!" );
        }
    else  {
        NSLog(@ "打開數據庫失敗!" );
    }
}
  • 執行指令

使用 sqlite3_exec() 方法能夠執行任何SQL語句,好比創表、更新、插入和刪除操做。可是通常不用它執行查詢語句,由於它不會返回查詢到的數據。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
*  往表中插入1000條數據
*/
- (void)insertData {
NSString *nameStr;
NSInteger age;
for  (NSInteger i = 0; i < 1000; i++) {
   nameStr = [NSString stringWithFormat:@ "Bourne-%d" , arc4random_uniform(10000)];
   age = arc4random_uniform(80) + 20;
   NSString *sql = [NSString stringWithFormat:@ "INSERT INTO t_person (name, age) VALUES('%@', '%ld')" , nameStr, age];
   char *errmsg = NULL;
   sqlite3_exec(_sqlite3, sql.UTF8String, NULL, NULL, &errmsg);
   if  (errmsg) {
       NSLog(@ "錯誤:%s" , errmsg);
   }
}
NSLog(@ "插入完畢!" );
}
  • 查詢指令

前面說過通常不使用 sqlite3_exec() 方法查詢數據。由於查詢數據必需要得到查詢結果,因此查詢相對比較麻煩。示例代碼以下:

    • sqlite3_prepare_v2() : 檢查sql的合法性

    • sqlite3_step() : 逐行獲取查詢結果,不斷重複,直到最後一條記錄

    • sqlite3_coloum_xxx() : 獲取對應類型的內容,iCol對應的就是SQL語句中字段的順序,從0開始。根據實際查詢字段的屬性,使用sqlite3_column_xxx取得對應的內容便可。

    • sqlite3_finalize() : 釋放stmt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
*  從表中讀取數據到數組中
*/
- (void)readData {
    NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1000];
    char *sql =  "select name, age from t_person;" ;
    sqlite3_stmt *stmt;
    NSInteger result = sqlite3_prepare_v2(_sqlite3, sql, -1, &stmt, NULL);
    if  (result == SQLITE_OK) {
        while  (sqlite3_step(stmt) == SQLITE_ROW) {
            char *name = (char *)sqlite3_column_text(stmt, 0);
            NSInteger age = sqlite3_column_int(stmt, 1);
            //建立對象
            Person *person = [Person personWithName:[NSString stringWithUTF8String:name] Age:age];
            [mArray addObject:person];
        }
        self.dataList = mArray;
    }
    sqlite3_finalize(stmt);
}

4.總結

總得來講,SQLite3的使用仍是比較麻煩的,由於都是些c語言的函數,理解起來有些困難。不過在通常開發過程當中,使用的都是第三方開源庫 FMDB,封裝了這些基本的c語言方法,使得咱們在使用時更加容易理解,提升開發效率。

FMDB

1.簡介

FMDB是iOS平臺的SQLite數據庫框架,它是以OC的方式封裝了SQLite的C語言API,它相對於cocoa自帶的C語言框架有以下的優勢:

使用起來更加面向對象,省去了不少麻煩、冗餘的C語言代碼

對比蘋果自帶的Core Data框架,更加輕量級和靈活

提供了多線程安全的數據庫操做方法,有效地防止數據混亂

注:FMDB的gitHub地址

2.核心類

FMDB有三個主要的類:

  • FMDatabase

一個FMDatabase對象就表明一個單獨的SQLite數據庫,用來執行SQL語句

  • FMResultSet

使用FMDatabase執行查詢後的結果集

  • FMDatabaseQueue

用於在多線程中執行多個查詢或更新,它是線程安全的

3.打開數據庫

和c語言框架同樣,FMDB經過指定SQLite數據庫文件路徑來建立FMDatabase對象,但FMDB更加容易理解,使用起來更容易,使用以前同樣須要導入sqlite3.dylib。打開數據庫方法以下:

1
2
3
4
5
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@ "person.db" ];
FMDatabase *database = [FMDatabase databaseWithPath:path];    
if  (![database open]) {
     NSLog(@ "數據庫打開失敗!" );
}

值得注意的是,Path的值能夠傳入如下三種狀況:

  • 具體文件路徑,若是不存在會自動建立

  • 空字符串@"",會在臨時目錄建立一個空的數據庫,當FMDatabase鏈接關閉時,數據庫文件也被刪除

  • nil,會建立一個內存中臨時數據庫,當FMDatabase鏈接關閉時,數據庫會被銷燬

4.更新

在FMDB中,除查詢之外的全部操做,都稱爲「更新」, 如:create、drop、insert、update、delete等操做,使用executeUpdate:方法執行更新:

1
2
3
4
5
6
7
8
//經常使用方法有如下3種:   
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
//示例
[database executeUpdate:@ "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)" ];   
//或者  
[database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES(?, ?)" , @ "Bourne" , [NSNumber numberWithInt:42]];

5.查詢

查詢方法也有3種,使用起來至關簡單:

1
2
3
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments

查詢示例:

1
2
3
4
5
6
7
//1.執行查詢
FMResultSet *result = [database executeQuery:@ "SELECT * FROM t_person" ];
//2.遍歷結果集
while  ([result next]) {
     NSString *name = [result stringForColumn:@ "name" ];
     int age = [result intForColumn:@ "age" ];
}

6.線程安全

在多個線程中同時使用一個FMDatabase實例是不明智的。不要讓多個線程分享同一個FMDatabase實例,它沒法在多個線程中同時使用。 若是在多個線程中同時使用一個FMDatabase實例,會形成數據混亂等問題。因此,請使用 FMDatabaseQueue,它是線程安全的。如下是使用方法:

  • 建立隊列。

1
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
  • 使用隊列

1
2
3
4
5
6
7
8
[queue inDatabase:^(FMDatabase *database) {    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_1" , [NSNumber numberWithInt:1]];    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_2" , [NSNumber numberWithInt:2]];    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_3" , [NSNumber numberWithInt:3]];      
           FMResultSet *result = [database executeQuery:@ "select * from t_person" ];    
          while ([result next]) {   
          }    
}];

並且能夠輕鬆地把簡單任務包裝到事務裏:

1
2
3
4
5
6
7
8
9
10
[queue inTransaction:^(FMDatabase *database, BOOL *rollback) {    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_1" , [NSNumber numberWithInt:1]];    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_2" , [NSNumber numberWithInt:2]];    
           [database executeUpdate:@ "INSERT INTO t_person(name, age) VALUES (?, ?)" , @ "Bourne_3" , [NSNumber numberWithInt:3]];      
           FMResultSet *result = [database executeQuery:@ "select * from t_person" ];    
              while ([result next]) {   
              }   
            //回滾
            *rollback = YES;  
     }];

FMDatabaseQueue 後臺會創建系列化的G-C-D隊列,並執行你傳給G-C-D隊列的塊。這意味着 你從多線程同時調用調用方法,GDC也會按它接收的塊的順序來執行。

 

 

p5:CoreData

 

1、CoreData的簡單使用

準備工做

  • 建立數據庫

    1. 新建文件,選擇CoreData -> DataModel
    2. 添加實體(表),Add Entity
    3. 給表中添加屬性,點擊Attributes下方的‘+’
  • 建立模型文件

    1. 新建文件,選擇CoreData -> NSManaged Object subclass
    2. 根據提示,選擇實體
  • 經過代碼,關聯數據庫和實體

    - (void)viewDidLoad { [super viewDidLoad]; /* * 關聯的時候,若是本地沒有數據庫文件,Coreadata本身會建立 */ // 1. 上下文 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; // 2. 上下文關連數據庫 // 2.1 model模型文件 NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; // 2.2 持久化存儲調度器 // 持久化,把數據保存到一個文件,而不是內存 NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 2.3 設置CoreData數據庫的名字和路徑 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *sqlitePath = [doc stringByAppendingPathComponent:@"company.sqlite"]; [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlitePath] options:nil error:nil]; context.persistentStoreCoordinator = store; _context = context; }

CoreData的基本操做(CURD)

  • 添加元素 - Create

    -(IBAction)addEmployee{ // 建立一個員工對象 //Employee *emp = [[Employee alloc] init]; 不能用此方法建立 Employee *emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context]; emp.name = @"wangwu"; emp.height = @1.80; emp.birthday = [NSDate date]; // 直接保存數據庫 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); } }
  • 讀取數據 - Read

    -(IBAction)readEmployee{ // 1.FetchRequest 獲取請求對象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2.設置過濾條件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"zhangsan"]; request.predicate = pre; // 3.設置排序 // 身高的升序排序 NSSortDescriptor *heigtSort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:NO]; request.sortDescriptors = @[heigtSort]; // 4.執行請求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } //NSLog(@"%@",emps); //遍歷員工 for (Employee *emp in emps) { NSLog(@"名字 %@ 身高 %@ 生日 %@",emp.name,emp.height,emp.birthday); } }
  • 修改數據 - Update

    -(IBAction)updateEmployee{ // 改變zhangsan的身高爲2m // 1.查找到zhangsan // 1.1FectchRequest 抓取請求對象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 1.2設置過濾條件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"zhangsan"]; request.predicate = pre; // 1.3執行請求 NSArray *emps = [_context executeFetchRequest:request error:nil]; // 2.更新身高 for (Employee *e in emps) { e.height = @2.0; } // 3.保存 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); } }
  • 刪除數據 - Delete

    -(IBAction)deleteEmployee{ // 刪除 lisi // 1.查找lisi // 1.1FectchRequest 抓取請求對象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 1.2設置過濾條件 // 查找zhangsan NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@", @"lisi"]; request.predicate = pre; // 1.3執行請求 NSArray *emps = [_context executeFetchRequest:request error:nil]; // 2.刪除 for (Employee *e in emps) { [_context deleteObject:e]; } // 3.保存 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); } }

2、CoreData的表關聯

準備工做

  • 建立數據庫

    1. 新建文件,選擇CoreData -> DataModel
    2. 添加實體(表),Add Entity注意:這裏根據關聯添加多個實體
    3. 給表中添加屬性,點擊Attributes下方的‘+’
  • 建立模型文件

    1. 新建文件,選擇CoreData -> NSManaged Object subclass
    2. 根據提示,選擇實體,注意:這裏先選擇被關聯的實體,最後添加最上層的實體
  • 經過代碼,關聯數據庫和實體

    - (void)viewDidLoad { [super viewDidLoad]; /* * 關聯的時候,若是本地沒有數據庫文件,Coreadata本身會建立 */ // 1. 上下文 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; // 2. 上下文關連數據庫 // 2.1 model模型文件 NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; // 2.2 持久化存儲調度器 // 持久化,把數據保存到一個文件,而不是內存 NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 2.3 設置CoreData數據庫的名字和路徑 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *sqlitePath = [doc stringByAppendingPathComponent:@"company.sqlite"]; [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlitePath] options:nil error:nil]; context.persistentStoreCoordinator = store; _context = context; }

基本操做

  • 添加元素 - Create

    -(IBAction)addEmployee{ // 1. 建立兩個部門 ios android //1.1 iOS部門 Department *iosDepart = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:_context]; iosDepart.name = @"ios"; iosDepart.departNo = @"0001"; iosDepart.createDate = [NSDate date]; //1.2 Android部門 Department *andrDepart = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:_context]; andrDepart.name = @"android"; andrDepart.departNo = @"0002"; andrDepart.createDate = [NSDate date]; //2. 建立兩個員工對象 zhangsan屬於ios部門 lisi屬於android部門 //2.1 zhangsan Employee *zhangsan = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context]; zhangsan.name = @"zhangsan"; zhangsan.height = @(1.90); zhangsan.birthday = [NSDate date]; zhangsan.depart = iosDepart; //2.2 lisi Employee *lisi = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_context]; lisi.name = @"lisi"; lisi.height = @2.0; lisi.birthday = [NSDate date]; lisi.depart = andrDepart; //3. 保存數據庫 NSError *error = nil; [_context save:&error]; if (error) { NSLog(@"%@",error); } }
  • 讀取信息 - Read

    -(IBAction)readEmployee{ // 讀取ios部門的員工 // 1.FectchRequest 抓取請求對象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2.設置過濾條件 NSPredicate *pre = [NSPredicate predicateWithFormat:@"depart.name = %@",@"android"]; request.predicate = pre; // 4.執行請求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } //遍歷員工 for (Employee *emp in emps) { NSLog(@"名字 %@ 部門 %@",emp.name,emp.depart.name); } }
  • 其餘功能與前幾種相似,這裏不在贅述

3、CoreData的模糊查詢

準備工做和上面相似,主要是查詢方式不一樣

  • 模糊查詢

    -(IBAction)readEmployee{ // 1.FectchRequest 抓取請求對象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2.設置排序 // 按照身高的升序排序 NSSortDescriptor *heigtSort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:NO]; request.sortDescriptors = @[heigtSort]; // 3.模糊查詢 // 3.1 名字以"wang"開頭 // NSPredicate *pre = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@",@"wangwu1"]; // request.predicate = pre; // 名字以"1"結尾 // NSPredicate *pre = [NSPredicate predicateWithFormat:@"name ENDSWITH %@",@"1"]; // request.predicate = pre; // 名字包含"wu1" // NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS %@",@"wu1"]; // request.predicate = pre; // like 匹配 NSPredicate *pre = [NSPredicate predicateWithFormat:@"name like %@",@"*wu12"]; request.predicate = pre; // 4.執行請求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } //遍歷員工 for (Employee *emp in emps) { NSLog(@"名字 %@ 身高 %@ 生日 %@",emp.name,emp.height,emp.birthday); } }
  • 分頁查詢

    -(void)pageSeacher{ // 1. FectchRequest 抓取請求對象 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 2. 設置排序 // 身高的升序排序 NSSortDescriptor *heigtSort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:NO]; request.sortDescriptors = @[heigtSort]; // 3. 分頁查詢 // 總有共有15數據 // 每次獲取6條數據 // 第一頁 0,6 // 第二頁 6,6 // 第三頁 12,6 3條數據 // 3.1 分頁的起始索引 request.fetchOffset = 12; // 3.2 分頁的條數 request.fetchLimit = 6; // 4. 執行請求 NSError *error = nil; NSArray *emps = [_context executeFetchRequest:request error:&error]; if (error) { NSLog(@"error"); } // 5. 遍歷員工 for (Employee *emp in emps) { NSLog(@"名字 %@ 身高 %@ 生日 %@",emp.name,emp.height,emp.birthday); } }

4、多個數據庫的使用

注意:

建立多個數據庫,即建立多個DataModel
一個數據庫對應一個上下文
須要根據bundle名建立上下文
添加或讀取信息,須要根據不一樣的上下文,訪問不一樣的實體

  • 關聯數據庫和實體

    - (void)viewDidLoad { [super viewDidLoad]; // 一個數據庫對應一個上下文 _companyContext = [self setupContextWithModelName:@"Company"]; _weiboContext = [self setupContextWithModelName:@"Weibo"]; } /** * 根據模型文件,返回一個上下文 */ -(NSManagedObjectContext *)setupContextWithModelName:(NSString *)modelName{ // 1. 上下文 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init]; // 2. 上下文關連數據庫 // 2.1 model模型文件 // 注意:若是使用下面的方法,若是 bundles爲nil 會把bundles裏面的全部模型文件的表放在一個數據庫 //NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; // 改成如下的方法獲取: NSURL *companyURL = [[NSBundle mainBundle] URLForResource:modelName withExtension:@"momd"]; NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:companyURL]; // 2.2 持久化存儲調度器 // 持久化,把數據保存到一個文件,而不是內存 NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 2.3 告訴Coredata數據庫的名字和路徑 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *sqliteName = [NSString stringWithFormat:@"%@.sqlite",modelName]; NSString *sqlitePath = [doc stringByAppendingPathComponent:sqliteName]; [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlitePath] options:nil error:nil]; context.persistentStoreCoordinator = store; // 3. 返回上下文 return context; }
  • 添加元素

    -(IBAction)addEmployee{ // 1. 添加員工 Employee *emp = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:_companyContext]; emp.name = @"zhagsan"; emp.height = @2.3; emp.birthday = [NSDate date]; // 直接保存數據庫 [_companyContext save:nil]; // 2. 發微博 Status *status =[NSEntityDescription insertNewObjectForEntityForName:@"Status" inManagedObjectContext:_weiboContext]; status.text = @"發了一條微博!"; status.createDate = [NSDate date]; [_weiboContext save:nil]; }

 

 
 
相關連接: iOS中的數據存儲(上)
               iOS中幾種數據持久化方案
              CoreData的簡單使用
      IOS MagicRecord 詳解
相關文章
相關標籤/搜索