iOS開發中的4種數據持久化方式【2、數據庫 SQLite三、Core Data 的運用】

               在上文,咱們介紹了ios開發中的其中2種數據持久化方式:屬性列表、歸檔解檔本節將繼續介紹另外2種iOS持久化數據的方法:數據庫 SQLite三、Core Data 的運用html

在本節,將經過對4個文本框內容的建立、修改,退出後臺,再從新回到後臺,來認識這兩種持久化數據的方式。效果圖以下【圖1】:ios

 

【圖1 GUI界面效果圖】git

【本次開發環境: Xcode:7.2     iOS Simulator:iphone6S plus   By:啊左】    github

 

1、數據庫SQLite3sql

SQLite(Strutcured Query Language,結構化查詢語言),是iOS的嵌入式SQL數據庫,在存儲和檢索大量數據方面很是有效,屬於輕量級數據庫,可是功能很強大。 安卓和ios開發使用的都是SQLite數據庫。    而另外一種持久化數據方式,core Data是對SQLite的封裝,由於iOS中使用的SQLite是純C語言的。數據庫

 

一、連接到SQLite3庫數組

在Xcode中,使用Single View Application模板建立一個新項目,命名爲persistence3。網絡

新建項目選中項目導航列表(最左邊)的頂部而後在主區域的TARGETS部分選中persistence3,注意要從TARGETS選中而不是從PROJECT部分。app

選中後,點擊「Build Phases」,打開在第三欄,按「+」添加「libsqlite3.0.tbd」【注意:Xcode7後dylib後綴改爲tbd,若是仍要添加.bylib爲後綴的連接,在添加framework那個對話框,最下面有個 "add other..." 點開以後, 按command+shift+G , 路徑輸入 /usr/lib/  ,而後 找到你須要的lib文件 ok了。。 好吧,我仍是習慣添加.dyib...框架

【圖2 連接SQLite3.dyib】

3是版本號,是SQLite的第三個版本。它是始終指向最新版本的SQLite3庫的;
 
在「Main.storyboard」中拖入4個標籤、4個文本框控件,拖動並對齊標籤與文本框,並依次修改標籤文本如【圖1】,「ViewController.h」中添加一個裝載4個文本框的數組「lineFields
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController

@property (nonatomic,strong)IBOutletCollection(UITextField) NSArray *lineFields;//存儲4個文本框字段的數組
@end

而後打開輔助編輯器,經過control鍵將4個文本框鏈接到 lineFields 這個數組,確保鏈接順序爲從頂部到底部!

 

在項目導航面板中,點擊"ViewController.m" ,將如下2段代碼添加到 @implementation與 @end 的中間,與上文相同,這個方法在後面會一直調用:

#import "ViewController.h"
#import <sqlite3.h>         //導入SQLite3,注意是擴折號

//SQLite 是不區分大小寫的

@implementation ViewController
{
    sqlite3 *sqlite; //數據庫
}

//懶加載
-(NSString *)datafilePath
{
    NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [array objectAtIndex:0];
    return [path stringByAppendingPathComponent:@"data.sqlite"];
}

在這裏,咱們介紹一下iOS9的一個新的變化:UIAlertController。小小地在這裏運用一下:

//警告提示框,爲後面的操做向用戶提示信息
-(void)alert:(NSString *)mes
{
    /*知識點:ios 9.0 後,簡單的UIAlertView已經不能用了。
     UIAlertController代替了UIAlertView彈框 和 UIActionSheet下彈框
     */
    //UIAlertControllerStyleAlert:中間;  UIAlertControllerStyleActionSheet:顯示在屏幕底部;
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"警告" message:mes preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:nil];
    UIAlertAction *defult = [UIAlertAction actionWithTitle:@"肯定" style:(UIAlertActionStyleDefault) handler:nil];
    [alert addAction:cancel];
    [alert addAction:defult];
    [self presentViewController:alert animated:YES completion:nil]; //呈現
}

 

- (void)viewDidLoad 以及通知的方法代碼:

 1 - (void)viewDidLoad {  2  [super viewDidLoad];  3     int result = sqlite3_open([[self datafilePath]UTF8String], &sqlite);  4     //不等於SQLITE_OK,則表示打開數據庫的時候遇到問題
 5     if(result != SQLITE_OK)  6  {  7  sqlite3_close(sqlite);  8         [self alert:@"數據庫打開失敗"];  9  } 10     
11     //定義一個語句,其中if not exists表示:若是不存在數據表,則新建一個。 若存在,則此命令自動退出. 因此這個語句能夠在每次啓動時調用
12     NSString *createSql = @"CREATE TABLE IF NOT EXISTS 'wenbenkuang'(id INTEGER PRIMARY KEY,datatext TEXT NOT NULL)"; 13     char * error; 14     int ret = sqlite3_exec(sqlite,[createSql UTF8String], NULL, NULL, &error); //SQLite是純C語言的。SQL語句須要使用「UTF8String」方法把NSString轉換爲char.
15     if(ret != SQLITE_OK) 16  { 17         [self alert:[NSString stringWithFormat:@"數據表建立失敗%s",error]]; 18  } 19     
20     //使用select語句加載數據,並要求數據庫按行號準備排序,以便咱們以相同的順序獲取,不然將使用sqlite3內部存儲順序
21     NSString *preSql = @"SELECT id,datatext FROM 'wenbenkuang'ORDER BY id"; 22     sqlite3_stmt *statmt; 23     if(sqlite3_prepare_v2(sqlite,[preSql UTF8String], -1, &statmt, nil) == SQLITE_OK) //SQLITE_OK表成功加載 24  { 25         while (sqlite3_step(statmt) == SQLITE_ROW) 26  { 27             int row = sqlite3_column_int(statmt, 0);                //獲取行號
28             char *rowData = (char *)sqlite3_column_text(statmt, 1); //獲取該行數據
29             NSString *dataString = [[NSString alloc]initWithUTF8String:rowData]; 30             UITextField *textfield = self.lineFields[row]; 31             textfield.text = dataString; 32  } 33         //完成陳述
34  sqlite3_finalize(statmt); 35  } 36     //關閉數據庫
37  sqlite3_close(sqlite); 38     
39     //註冊一個觀測者,進入後臺時發送通知;
40     UIApplication *app = [UIApplication sharedApplication]; 41     [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillResignActiveNotification:) 
name:UIApplicationWillResignActiveNotification object:app]; 42 } 43 -(void)applicationWillResignActiveNotification:(NSNotification *)notification 44 { 45 int result = sqlite3_open([[self datafilePath] UTF8String], &sqlite); 46 if (result != SQLITE_OK) { 47 [self alert:@"數據庫打開失敗"]; 48 sqlite3_close(sqlite); 49 } 50 for(int i=0;i<4;i++) 51 { 52 UITextField *tetxField = self.lineFields[i]; 53 char *updataSql = "INSERT OR REPLACE INTO 'wenbenkuang'(id,datatext) VALUES(?,?);"; 54 sqlite3_stmt *stmt; 55 if(sqlite3_prepare_v2(sqlite, updataSql, -1, &stmt, nil) == SQLITE_OK) 56 { 57 sqlite3_bind_int(stmt, 1, i); 58 sqlite3_bind_text(stmt, 2, [tetxField.text UTF8String], -1, NULL); 59 } 60 if(sqlite3_step(stmt) != SQLITE_DONE) 61 { 62 [self alert:@"數據更新失敗"]; 63 } 64 sqlite3_finalize(stmt); 65 } 66 sqlite3_close(sqlite); 67 }

在這段的viewDidLoad代碼中:

(行號爲3-9)咱們首先建立或者打開數據庫,若是打開時遇到問題,則拋出警告框。

(行號爲11-18)數據庫將全部的數據存儲在表中。所以,建立一個名爲「wenbenkuang」數據表,包含一個標識爲「id」的鍵,與一個名爲"datatext"的不爲空的文本項,若是已存在相同名稱的表,則退出建立,不執行操做,因此該數據庫語句能夠在每次啓動時調用一次,而不會影響到現有的數據庫;【SQLite是純C語言的。SQL語句須要使用「UTF8String」方法把NSString轉換爲char.】

(行號爲20-23)加載數據,使select語句加載數據,並要求數據庫按行號準備排序,以便咱們以相同的順序獲取,不然將使用sqlite3內部存儲順序;
 
(行號爲25-32)遍歷返回各行,定義一個int和char獲取數據,而後,咱們經過從數據庫獲取的數據設置咱們的文本字段。
 
最後,關閉數據庫,操做結束;
 
在 「applicationWillResignActiveNotification:」 方法中,咱們也是首先打開數據庫,建立一個字段名稱,以便檢測到輸出,而後設計一條帶2個綁定變量的INSERT OR REPLACE的SQL語句,第一變量表行,第二個表存儲的實際字段值,接下來聲明一個指向語句的指針,爲語句添加綁定變量,並將值綁定到2個綁定標量中,
經過調用sqlite3_step來執行更新,循環執行該語句。
完成後,關閉操做數據庫;接下來咱們運行調試一下。
 
按「command+R」運行程序,爲4個文本框輸入字段,關閉Xcode,或按下「command+shift」,點擊2次「H」鍵,退出該應用,而後再次打開,
看看是是否文本框中保留有原來本身的輸入的字段。
 
2、Core Data的運用
 
首先,咱們須要注意的是,Core Data是iOS上一個可以提升效率的數據庫框架,但Core Data不是一種數據庫,它的底層仍是利用Sqlite3來存儲數據的。
一樣的,此次咱們依然經過建立一個簡單的persistence應用,來展現如何經過蘋果自帶的Core Data框架來實現持久化。 
第一步,咱們應該很熟悉了。在Xcode中,使用Single View Application模板建立一個新項目,命名爲persistence4。
如【圖3】,先別按Next,勾選「Use Core Data」,其中「Devices」爲設備選項,你能夠選擇「iphone」或者「ipad」,這裏咱們選擇通用「Universal」。點擊Next!
【圖3 建立persistence4】
此次在敲入代碼前,咱們還須要進行一系列的討論,好吧。。。「Core Data」確實麻煩些。 
一、Core Data的數據模型
以前,如上文的iOS開發中的4種數據持久化方式【1、屬性列表與歸檔解檔】所介紹的,在使用Core Data以前咱們建立數據模型的方式即是建立一個NSObject的子類並讓它們遵循NSCoding和NSCopying協議,以便可以對它們進行歸檔解檔。可是,在Core Data中,咱們使用了不一樣方式,不須要建立一個新子類,而是先在數據模型中建立實體(Entity),而後在代碼中爲這些實體建立託管對象(Managed Object)
實體,就是對對象的描述;   託管對象,表示在運行的過程該實體的具體實例;
(能夠這麼理解:實體、託管對象之間的關係相似於類與類的對象;)
 
二、鍵-值編碼形式
在咱們使用NSDictionary的時候,就已經使用到這種編碼的形式了,但與NSDictionary相比,Core Data會複雜一些,只是基本概念是相同的。具體的操做方法以下:
首先定義一個託管對象: NSManagedObject *managedObject;
那麼,就能夠經過相應的方法獲取name特性的數據值了:
NSString *nameData = [managedObject valueForKey:@"name"];
爲name特性設置新的屬性值:
[managedObject setValue:@"newNameData" forKey:@"name"];

 

三、動手:模型的建立。

在左邊的項目導航面板上面單擊「persistence4.xcdatamodeld」文件,此時打開了Xcode的數據模型編輯器,編輯器的面板中已經列出了數據模型中的全部實體、獲取請求和配置。

因爲咱們還未建立數據模型,所以列表是空的,單擊實體面板左下方的加號圖標(Add Entity),此時建立了一個名爲:「Entity」的實體:

(提醒一下的是,右下角的「Editor Style」選項:table視圖和graph視圖。這兩種視圖在數據模型上沒有區別,只是顯示的方式不一樣而已。若是你的模型裏面包含多個實體,那麼graph視圖的顯示方式會很是有用,它以圖形化的方式呈現了全部實體之間的關係;因爲table視圖顯示了當前實體更爲詳細的信息,由於咱們在建立這個實體的時候,仍是選用默認的table實體)

 

而後點擊該實體,在右邊的數據模型編輯器上面,把第一個name字段改成咱們接下來使用的「Line」字段,因此咱們這樣就算建立了一個名爲「Line」的實體了。

 

接下來就爲「Line」實體添加新特性,單擊並按住右下角的加號圖標「Add Attribute」,固然添加特性的話也能夠直接點擊「+」圖標就能夠了,這裏只是爲了方便讀者看到該選項所表達的意思。

 

點擊「+」後,能夠看到新增了一個名爲「attribute」的特性,把它修改成「lineNumber」,修改Type爲「Interger16」,並把右邊圈紅的Optional取消選中狀態。

 

再次單擊「+」,把新特性修改成「lineText」,修改Type爲「String」,這裏的Optional默認勾選(該選項用於防止咱們建立的「lineText」文本在用戶給定的字段爲空,而「lineNumber」錶行號,行號不會出現爲空的狀況,)

下面是代碼部分,與SQLite的建立相似:

在「Main.storyboard」中拖入4個標籤、4個文本框控件,拖動並對齊標籤與文本框,並依次修改標籤文本如【圖1】,「ViewController.h」中添加一個裝載4個文本框的數組「lineFields

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController

@property (nonatomic,strong)IBOutletCollection(UITextField) NSArray *lineFields; //存儲4個文本框字段的數組

@end

而後打開輔助編輯器,經過control鍵將4個文本框鏈接到 lineFields 這個數組,確保鏈接順序爲從頂部到底部!

單擊「AppDelegate.h」,咱們可以已經包含了數據類型定義須要的代碼了,咱們作一些註釋:

//COREDATA託管上下文
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; //託管模型
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; //存儲時(持久化)協調者
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; //保存託管上下文
- (void)saveContext; //取得當前應用程序文檔路徑
- (NSURL *)applicationDocumentsDirectory;

 點擊「ViewController.m」,下面,咱們進行一連串的代碼:

 1 #import "ViewController.h"
 2 #import "AppDelegate.h"     //須要導入app代理類
 3 
 4 static NSString *const Zline       = @"Line";  5 static NSString *const ZlineNumber = @"lineNumber";  6 static NSString *const ZlineText   = @"lineText";  7 
 8 @implementation ViewController  9 
10 - (void)viewDidLoad { 11  [super viewDidLoad]; 12     //獲取應用程序代理
13     AppDelegate *appDe = [UIApplication sharedApplication].delegate; 14     //獲取託管對象上下文(此時若是數據庫不存在就不會建立數據庫)
15     NSManagedObjectContext *context = [appDe managedObjectContext]; 16     //建立請求(並傳遞實體描述「line」給它)
17     NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:Zline]; 18     //經過上下文context執行請求request,獲取記錄對象的數組
19     NSArray *objetcs  = [context executeFetchRequest:request error:nil]; 20     if(objetcs == nil)  //確保返回的是有效的數組
21  { 22         NSLog(@"數組建立失敗"); 23  } 24     //分別提取每一個託管對象保存的數據
25     for(NSManagedObject *managed in objetcs) 26  { 27         //獲取行號(注意轉換爲int)
28         int lineNum = [[managed valueForKey:ZlineNumber] intValue]; 29         //獲取文本
30         NSString *lineText = [managed valueForKey:ZlineText]; 31         
32         UITextField *textField = self.lineFields[lineNum]; 33         textField.text = lineText; 34  } 35 
36     //後臺處理
37     UIApplication *app = [UIApplication sharedApplication]; 38     [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillResignActiveNotification:) name:
UIApplicationWillResignActiveNotification object:app]; 39 } 40 41 -(void)applicationWillResignActiveNotification:(NSNotification *)notification 42 { 43 AppDelegate *appDe = [UIApplication sharedApplication].delegate; 44 NSManagedObjectContext *context = appDe.managedObjectContext; 45 46 for(int i =0;i<2;i++) 47 { 48 NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:Zline]; 49 //predicate謂語,建立請求,可是爲確認存儲中是否已有一個與字段對應的託管對象,建立謂語,是爲了給字段標識正確的對象。 50 request.predicate = [NSPredicate predicateWithFormat:@"(lineNumber=%d)",i]; //注意謂語的拼寫:@"(lineNumber = %d)",i 51 NSArray *objects = [context executeFetchRequest:request error:nil]; 52 if(objects ==nil) 53 { 54 NSLog(@"數組建立失敗"); 55 } 56 57 //由於咱們不知道是要從存儲中加載託管對象,仍是建立新的託管對象,所以先建立空的託管對象 58 NSManagedObject *managed = nil; 59 if([objects count]>0) //檢查返回有效的對象,所以加載 60 { 61 managed = [objects objectAtIndex:0]; 62 } 63 else //檢查到無有效對象,所以建立新的託管對象 64 { 65 //建立實體、插入托管對象到獲取的上下文,咱們直接用下面這句代碼,省去不少流程 66 managed = [NSEntityDescription insertNewObjectForEntityForName:Zline inManagedObjectContext:context]; 67 } 68 UITextField *textField = self.lineFields[i]; 69 //使用鍵-值碼方式更新設置行號和文本: 70 [managed setValue:[NSNumber numberWithInt:i] forKey:ZlineNumber]; 71 [managed setValue:textField.text forKey:ZlineText]; 72 } 73 //完成循環。 74 //最後一步:持久化數據: 75 [appDe saveContext]; 76 } 77 78 @end

咱們解析一下上面的代碼,首先須要導入咱們建立Core Data模型時Xcode建立的已有代碼的「AppDelegate.h」。

(4-6行)    :定義包括實體「Line」、行號「lineNumber」、文本「lineText」等的字符段,方便咱們後面的代碼編寫;(記得別拼錯,別問我爲何怎麼說...我就是剛剛在演示的時候一直報錯,才發現原來只是字符串拼錯了...555..心疼啊左);

(12-23行): 經過上下文context,執行請求request獲取記錄對象的數組。並確認數組有效。

(24-34行):分別提取每一個託管對象保存的數據,並賦值給對應行號的文本框文本字段;

(36-39行):後臺處理

(48-55行):執行帶有謂語的請求。

(57-71行):分託管對象是否已經存在2種狀況,進行編碼設置行號和文本

(75行):     持久化數據(記得帶上這行代碼)

 

好了,關於Core Data數據模型和GUI界面,以及代碼咱們已經設計編寫完畢。

按「command+R」運行程序,爲4個文本框輸入字段,關閉Xcode,或按下「command+shift」,點擊2次「H」鍵,退出該應用,而後再次打開,
若是運行沒有發生錯誤的話,那麼咱們已經設計了一個簡單的Core Data儲存數據應用「persistence4」了。
 
 
 
至此,咱們包括上文iOS開發中的4種數據持久化方式【1、屬性列表與歸檔解檔】的演練,已經爲你們展現了有關iOS開發中的4種數據持久化方式。
但願可以分享與讀者。
只是,對於這兩篇關於iOS開發數據持久化的所搭建的應用"persistence"例子,也只是一個簡單的應用,具體的應用設計你們能夠發揮創造力,讓數據持久化在移動開發應用過程當中更加便捷。
固然,更多更深刻的數據持久化、數據處理,咱們只懂得這些是遠遠不夠的,須要咱們經過網絡上共享的內容以及本身工做學習中不斷積累,才能使這些知識點更加深刻,更加適應iOS開發工做。
相關文章
相關標籤/搜索