在上文,我们介绍了ios开发中的其中2种数据持久化方式:属性列表、归档解档本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3、Core Data 的运用

在本节,将通过对4个文本框内容的创建、修改,退出后台,再重新回到后台,来认识这两种持久化数据的方式。效果图如下【图1】:

【图1 GUI界面效果图】

【本次开发环境: Xcode:7.2     iOS Simulator:iphone6S plus   By:啊左】    

一、数据库SQLite3

SQLite(Strutcured Query Language,结构化查询语言),是iOS的嵌入式SQL数据库,在存储和检索大量数据方面非常有效,属于轻量级数据库,但是功能很强大。 安卓和ios开发使用的都是SQLite数据库。    而另一种持久化数据方式,core Data是对SQLite的封装,因为iOS中使用的SQLite是纯C语言的。

1、链接到SQLite3库

在Xcode中,使用Single View Application模板创建一个新项目,命名为persistence3。

新建项目选中项目导航列表(最左边)的顶部然后在主区域的TARGETS部分选中persistence3,注意要从TARGETS选中而不是从PROJECT部分。

选中后,点击“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:];
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 以及通知的方法代码:

 - (void)viewDidLoad {
[super viewDidLoad];
int result = sqlite3_open([[self datafilePath]UTF8String], &sqlite);
//不等于SQLITE_OK,则表示打开数据库的时候遇到问题
if(result != SQLITE_OK)
{
sqlite3_close(sqlite);
[self alert:@"数据库打开失败"];
} //定义一个语句,其中if not exists表示:如果不存在数据表,则新建一个。 若存在,则此命令自动退出. 所以这个语句可以在每次启动时调用
NSString *createSql = @"CREATE TABLE IF NOT EXISTS 'wenbenkuang'(id INTEGER PRIMARY KEY,datatext TEXT NOT NULL)";
char * error;
int ret = sqlite3_exec(sqlite,[createSql UTF8String], NULL, NULL, &error); //SQLite是纯C语言的。SQL语句需要使用“UTF8String”方法把NSString转换为char.
     if(ret != SQLITE_OK)
{
[self alert:[NSString stringWithFormat:@"数据表创建失败%s",error]];
} //使用select语句加载数据,并要求数据库按行号准备排序,以便我们以相同的顺序获取,否则将使用sqlite3内部存储顺序
NSString *preSql = @"SELECT id,datatext FROM 'wenbenkuang'ORDER BY id";
sqlite3_stmt *statmt;
if(sqlite3_prepare_v2(sqlite,[preSql UTF8String], -, &statmt, nil) == SQLITE_OK) //SQLITE_OK表成功加载
{
while (sqlite3_step(statmt) == SQLITE_ROW)
{
int row = sqlite3_column_int(statmt, ); //获取行号
char *rowData = (char *)sqlite3_column_text(statmt, ); //获取该行数据
NSString *dataString = [[NSString alloc]initWithUTF8String:rowData];
UITextField *textfield = self.lineFields[row];
textfield.text = dataString;
}
//完成陈述
sqlite3_finalize(statmt);
}
//关闭数据库
sqlite3_close(sqlite); //注册一个观测者,进入后台时发送通知;
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillResignActiveNotification:)
name:UIApplicationWillResignActiveNotification object:app];
}
-(void)applicationWillResignActiveNotification:(NSNotification *)notification
{
int result = sqlite3_open([[self datafilePath] UTF8String], &sqlite);
if (result != SQLITE_OK) {
[self alert:@"数据库打开失败"];
sqlite3_close(sqlite);
}
for(int i=;i<;i++)
{
UITextField *tetxField = self.lineFields[i];
char *updataSql = "INSERT OR REPLACE INTO 'wenbenkuang'(id,datatext) VALUES(?,?);";
sqlite3_stmt *stmt;
if(sqlite3_prepare_v2(sqlite, updataSql, -, &stmt, nil) == SQLITE_OK)
{
sqlite3_bind_int(stmt, , i);
sqlite3_bind_text(stmt, , [tetxField.text UTF8String], -, NULL);
}
if(sqlite3_step(stmt) != SQLITE_DONE)
{
[self alert:@"数据更新失败"];
}
sqlite3_finalize(stmt);
}
sqlite3_close(sqlite);
}

在这段的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”键,退出该应用,然后再次打开,
看看是是否文本框中保留有原来自己的输入的字段。
 
二、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”确实麻烦些。 
1、Core Data的数据模型
之前,如上文的iOS开发中的4种数据持久化方式【一、属性列表与归档解档】所介绍的,在使用Core Data之前我们创建数据模型的方式便是创建一个NSObject的子类并让它们遵循NSCoding和NSCopying协议,以便能够对它们进行归档解档。但是,在Core Data中,我们使用了不同方式,不需要创建一个新子类,而是先在数据模型中创建实体(Entity),然后在代码中为这些实体创建托管对象(Managed Object)。
实体,就是对对象的描述;   托管对象,表示在运行的过程该实体的具体实例;
(可以这么理解:实体、托管对象之间的关系类似于类与类的对象;)
 
2、键-值编码形式
在我们使用NSDictionary的时候,就已经使用到这种编码的形式了,但与NSDictionary相比,Core Data会复杂一些,只是基本概念是相同的。具体的操作方法如下:
首先定义一个托管对象: NSManagedObject *managedObject;
那么,就可以通过相应的方法获取name特性的数据值了:

NSString *nameData = [managedObject valueForKey:@"name"];

为name特性设置新的属性值:

[managedObject setValue:@"newNameData" forKey:@"name"];

3、动手:模型的创建。

在左边的项目导航面板上面单击“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”,下面,我们进行一连串的代码:

 #import "ViewController.h"
#import "AppDelegate.h" //需要导入app代理类 static NSString *const Zline = @"Line";
static NSString *const ZlineNumber = @"lineNumber";
static NSString *const ZlineText = @"lineText"; @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
//获取应用程序代理
AppDelegate *appDe = [UIApplication sharedApplication].delegate;
//获取托管对象上下文(此时如果数据库不存在就不会创建数据库)
NSManagedObjectContext *context = [appDe managedObjectContext];
//创建请求(并传递实体描述“line”给它)
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:Zline];
//通过上下文context执行请求request,获取记录对象的数组
NSArray *objetcs = [context executeFetchRequest:request error:nil];
if(objetcs == nil) //确保返回的是有效的数组
{
NSLog(@"数组创建失败");
}
//分别提取每个托管对象保存的数据
for(NSManagedObject *managed in objetcs)
{
//获取行号(注意转换为int)
int lineNum = [[managed valueForKey:ZlineNumber] intValue];
//获取文本
NSString *lineText = [managed valueForKey:ZlineText]; UITextField *textField = self.lineFields[lineNum];
textField.text = lineText;
} //后台处理
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillResignActiveNotification:) name:
UIApplicationWillResignActiveNotification object:app];
} -(void)applicationWillResignActiveNotification:(NSNotification *)notification
{
AppDelegate *appDe = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDe.managedObjectContext; for(int i =;i<;i++)
{
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:Zline];
//predicate谓语,创建请求,但是为确认存储中是否已有一个与字段对应的托管对象,创建谓语,是为了给字段标识正确的对象。
request.predicate = [NSPredicate predicateWithFormat:@"(lineNumber=%d)",i]; //注意谓语的拼写:@"(lineNumber = %d)",i
NSArray *objects = [context executeFetchRequest:request error:nil];
if(objects ==nil)
{
NSLog(@"数组创建失败");
} //因为我们不知道是要从存储中加载托管对象,还是创建新的托管对象,因此先创建空的托管对象
NSManagedObject *managed = nil;
if([objects count]>) //检查返回有效的对象,因此加载
{
managed = [objects objectAtIndex:];
}
else //检查到无有效对象,因此创建新的托管对象
{
//创建实体、插入托管对象到获取的上下文,我们直接用下面这句代码,省去很多流程
managed = [NSEntityDescription insertNewObjectForEntityForName:Zline inManagedObjectContext:context];
}
UITextField *textField = self.lineFields[i];
//使用键-值码方式更新设置行号和文本:
[managed setValue:[NSNumber numberWithInt:i] forKey:ZlineNumber];
[managed setValue:textField.text forKey:ZlineText];
}
//完成循环。
//最后一步:持久化数据:
[appDe saveContext];
} @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种数据持久化方式【一、属性列表与归档解档】的演练,已经为大家展示了有关iOS开发中的4种数据持久化方式。
希望能够分享与读者。
只是,对于这两篇关于iOS开发数据持久化的所搭建的应用"persistence"例子,也只是一个简单的应用,具体的应用设计大家可以发挥创造力,让数据持久化在移动开发应用过程中更加便捷。
当然,更多更深入的数据持久化、数据处理,我们只懂得这些是远远不够的,需要我们通过网络上共享的内容以及自己工作学习中不断积累,才能使这些知识点更加深入,更加适应iOS开发工作。
 

iOS开发中的4种数据持久化方式【二、数据库 SQLite3、Core Data 的运用】的更多相关文章

  1. iOS开发中的4种数据持久化方式【一、属性列表与归档解档】

    iOS中的永久存储,也就是在关机重新启动设备,或者关闭应用时,不会丢失数据.在实际开发应用时,往往需要持久存储数据的,这样用户才能在对应用进行操作后,再次启动能看到自己更改的结果与痕迹.ios开发中, ...

  2. 四种数据持久化方式(下) :SQLite3 和 Core Data

    在上文,我们介绍了iOS开发中的其中2种数据持久化方式:属性列表.归档解档. 本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3.Core Data 的运用: 在本节,将通过对4个文 ...

  3. IOS开发数据存储篇—IOS中的几种数据存储方式

    IOS开发数据存储篇—IOS中的几种数据存储方式 发表于2016/4/5 21:02:09  421人阅读 分类: 数据存储 在项目开发当中,我们经常会对一些数据进行本地缓存处理.离线缓存的数据一般都 ...

  4. Android编程中的5种数据存储方式

    Android编程中的5种数据存储方式 作者:牛奶.不加糖 字体:[增加 减小] 类型:转载 时间:2015-12-03我要评论 这篇文章主要介绍了Android编程中的5种数据存储方式,结合实例形式 ...

  5. IOS开发中设置控件内容对齐方式时容易混淆的几个属性

    IOS开发中四个容易混淆的属性: 1. textAligment : 文字的水平方向的对齐方式 1> 取值 NSTextAlignmentLeft      = 0,    // 左对齐 NST ...

  6. IOS开发中的几种设计模式介绍

    ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现. 优势:解耦合 敏捷 ...

  7. IOS开发中的几种设计模式

    ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现.优势:解耦合敏捷原则 ...

  8. iOS 数据持久化(3):Core Data

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css); @import url(/ ...

  9. Android中的5种数据存储方式

    本文转自  http://hi.baidu.com/maguowei/blog/item/7aca46c25574a33ae5dd3ba4.htmlAndroid数据存储Android提供了5种方式存 ...

随机推荐

  1. redis 操作 hash 的测试

    1>hset setname field value hset stuSet name zhangsan:1        2>hget setname field hget stuset ...

  2. Matrix QR Decomposition using OpenCV

    Matrix QR decomposition is very useful in least square fitting model. But there is no function avail ...

  3. Mathematics:X-factor Chains(POJ 3421)

    X链条 题目大意,从1到N,1 = X0, X1, X2, …, Xm = X中间可以分成很多数,另Xi < Xi+1 Xi 可以整除Xi+1 ,求最大长度m和m长度的链有多少条 思路: 很简单 ...

  4. Linux(Centos)下jdbc连接oracle速度超慢的问题

    最近在centos下写个java swing程序,发现在linux用jdbc连接oracle及其缓慢,还经常失败.但是同样的程序在windows下运行就连接的非常快.网上搜索了很长时间都和我这情况没关 ...

  5. 解决两台虚拟机互ping可通,但connect失败

    问题描述: 在UNP一书中实例中,采用两台不同的虚拟机.即一台虚拟机作为服务端,另外一台虚拟机作为客户端. 现象: 两台电脑各自互ping可通 客户端访问local可行 客户机访问服务端报错:No r ...

  6. Building a Space Station

    poj2031:http://poj.org/problem?id=2031 题意:就是给出三维坐标系上的一些球的球心坐标和其半径,搭建通路,使得他们能够相互连通.如果两个球有重叠的部分则算为已连通, ...

  7. Python3玩转儿 机器学习(2)

    机器学习的基本任务 分类任务 回归任务 分类任务 手写输入数字识别 分类任务: 二分类任务 判断邮件是垃圾邮件或者不是垃圾邮件 判断发放给客户信用卡有风险或者没有风险 判断病患良性肿瘤还是恶性肿瘤 判 ...

  8. 解决vue在ios或android中用webview打开H5链接时#号后面的参数被忽略问题angular同样适用

    在ios或android如果直接用webview在打开H5链接例如: 打开:http://localhost:8080/#/answer?id=1509335039582001 会变成 http:// ...

  9. HTML 页面meta标签

    1. 概述 1.1 说明 <meta>标签提供了HTML文档的元数据[元数据(Metadata)是数据的数据信息],即页面的元信息,元数据不会显示在客户端,但是会被浏览器解析.meta元素 ...

  10. 项目中 2个或者多个EF模型 表名称相同会导致生成的实体类 覆盖的解决方法

    场景:  2个数据库, 一个新,一个旧,  把旧的 数据库中的数据,导入到新的数据库中,  使用到了2个 EF实体模型, 新数据库 和 旧数据库中的表,有的名称是相同的 (但是结构是不同的) 旧的数据 ...