iOS开发中的4种数据持久化方式【二、数据库 SQLite3、Core Data 的运用】
在上文,我们介绍了ios开发中的其中2种数据持久化方式:属性列表、归档解档。本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3、Core Data 的运用;
在本节,将通过对4个文本框内容的创建、修改,退出后台,再重新回到后台,来认识这两种持久化数据的方式。效果图如下【图1】:
【图1 GUI界面效果图】
【本次开发环境: Xcode:7.2 iOS Simulator:iphone6S plus By:啊左】
- (本文2个项目demo下载:数据库SQLite3的运用Demo 、Core Data 的运用Demo)
一、数据库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】
#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.】
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界面,以及代码我们已经设计编写完毕。
iOS开发中的4种数据持久化方式【二、数据库 SQLite3、Core Data 的运用】的更多相关文章
- iOS开发中的4种数据持久化方式【一、属性列表与归档解档】
iOS中的永久存储,也就是在关机重新启动设备,或者关闭应用时,不会丢失数据.在实际开发应用时,往往需要持久存储数据的,这样用户才能在对应用进行操作后,再次启动能看到自己更改的结果与痕迹.ios开发中, ...
- 四种数据持久化方式(下) :SQLite3 和 Core Data
在上文,我们介绍了iOS开发中的其中2种数据持久化方式:属性列表.归档解档. 本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3.Core Data 的运用: 在本节,将通过对4个文 ...
- IOS开发数据存储篇—IOS中的几种数据存储方式
IOS开发数据存储篇—IOS中的几种数据存储方式 发表于2016/4/5 21:02:09 421人阅读 分类: 数据存储 在项目开发当中,我们经常会对一些数据进行本地缓存处理.离线缓存的数据一般都 ...
- Android编程中的5种数据存储方式
Android编程中的5种数据存储方式 作者:牛奶.不加糖 字体:[增加 减小] 类型:转载 时间:2015-12-03我要评论 这篇文章主要介绍了Android编程中的5种数据存储方式,结合实例形式 ...
- IOS开发中设置控件内容对齐方式时容易混淆的几个属性
IOS开发中四个容易混淆的属性: 1. textAligment : 文字的水平方向的对齐方式 1> 取值 NSTextAlignmentLeft = 0, // 左对齐 NST ...
- IOS开发中的几种设计模式介绍
ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现. 优势:解耦合 敏捷 ...
- IOS开发中的几种设计模式
ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现.优势:解耦合敏捷原则 ...
- iOS 数据持久化(3):Core Data
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css); @import url(/ ...
- Android中的5种数据存储方式
本文转自 http://hi.baidu.com/maguowei/blog/item/7aca46c25574a33ae5dd3ba4.htmlAndroid数据存储Android提供了5种方式存 ...
随机推荐
- CloseableHttpResponse的使用
*************************** *这篇随手弄出来了,很急躁,有空再改 *************************** 基本逻辑是: 1.定义一个客户端 2.定义一个方法 ...
- USACO 08-Nov( 最小生成树)
美国人出题拐弯抹角,倒是挺尊重动物的 10206301 2 52 3 52 4 123 4 172 5 153 5 64 5 12 Hint从牧场4起床, 然后按照 4, 5, 4, 2, 3, 2, ...
- 从零开始学习Node.js例子六 EventEmitter发送和接收事件
pulser.js /* EventEmitter发送和接收事件 HTTPServer和HTTPClient类,它们都继承自EventEmitter EventEmitter被定义在Node的事件(e ...
- Oracle数据库初级学习
第一天 SQL查询语句 CMD——SQLPLUS----账号 soctt 密码 tiger,输入完成后进入SQL数据库,如图显示即连接成功. 连接成功后 ,我们首先设置一下基本界面, --设置 每列显 ...
- H5版俄罗斯方块(4)---火拼对战的雏形
前言: 勿忘初心, 本系列的目标是实现一款类似QQ"火拼系列"的人机对战版俄罗斯方块. 在完成了基本游戏框架和AI的算法探索后, 让我们来尝试一下人机大战雏形编写. 本系列的文章链 ...
- for循环相关
循环语句是指令式编程的常见语句,Scala对其加以改进,成为适应函数式风格的利器. for循环中的变量,没有val或者var,是因为变量的类型,完全是集合中的元素的类型.作用域持续到括号结束. 在sc ...
- Web Storage的方法
1.分为两种:localStorage与sessionStorage.2.存储形式:key-value的形式.sessionStorage 1.session定义:session指用户在浏览某个网站时 ...
- ✡ leetcode 163. Missing Ranges 找出缺失范围 --------- java
Given a sorted integer array where the range of elements are in the inclusive range [lower, upper], ...
- vue.js使用详解
1.什么是vue.jsvue.js是一款数据驱动型的js框架.何为数据驱动型?html视图层定义模板,vue定义数据.html和vue数据,通过标签id关联. 2.vue.js引入<script ...
- Windows环境下使用Redis缓存工具的图文详细方法
一.简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset(有序集合). ...