在上文,咱們介紹了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】
#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.】
NSString *nameData = [managedObject valueForKey:@"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界面,以及代碼咱們已經設計編寫完畢。