zoukankan      html  css  js  c++  java
  • 四种数据持久化方式(下) :SQLite3 和 Core Data

    在上文,我们介绍了iOS开发中的其中2种数据持久化方式:属性列表、归档解档。

    本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3、Core Data 的运用;

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

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

    (本节2个项目demo的下载:数据库SQLite3的运用Demo、Core Data 的运用Demo)

    https://github.com/Azuo520/persistence3

    https://github.com/Azuo520/persistence4

    一、数据库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…】

    3是版本号,是SQLite的第三个版本。它是始终指向最新版本的SQLite3库的;

    在“Main.storyboard”中拖入4个标签、4个文本框控件,拖动并对齐标签与文本框,并依次修改标签文本如【图1】,“ViewController.h”中添加一个装载4个文本框的数组“lineFields”:

    #import

    @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,注意是扩折号

    //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

    在这段的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的运用

    同样的,这次我们依然通过创建一个简单的persistence应用,来展示如何通过苹果自带的Core Data框架来实现持久化。

    第一步,我们应该很熟悉了。在Xcode中,使用Single View Application模板创建一个新项目,命名为persistence4。

    如【图3】,先别按Next,勾选“Use Core Data”,其中“Devices”为设备选项,你可以选择“iphone”或者“ipad”,这里我们选择通用“Universal”。点击Next!

    这次在敲入代码前,我们还需要进行一系列的讨论,好吧。。。“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

    @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;i0) //检查返回有效的对象,因此加载

    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种数据持久化方式【一、属性列表与归档解档】的演练,已经为大家展示了有关iOS开发中的4种数据持久化方式。

    希望能够分享与读者。

    只是,对于这两篇关于iOS开发数据持久化的所搭建的应用”persistence”例子,也只是一个简单的应用,具体的应用设计大家可以发挥创造力,让数据持久化在移动开发应用过程中更加便捷。

    当然,更多更深入的数据持久化、数据处理,我们只懂得这些是远远不够的,需要我们通过网络上共享的内容以及自己工作学习中不断积累,才能使这些知识点更加深入,更加适应iOS开发工作。

  • 相关阅读:
    ubuntu 安装 redis desktop manager
    ubuntu 升级内核
    Ubuntu 内核升级,导致无法正常启动
    spring mvc 上传文件,但是接收到文件后发现文件变大,且文件打不开(multipartfile)
    angular5 open modal
    POJ 1426 Find the Multiple(二维DP)
    POJ 3093 Margritas
    POJ 3260 The Fewest Coins
    POJ 1837 Balance(二维DP)
    POJ 1337 A Lazy Worker
  • 原文地址:https://www.cnblogs.com/fengmin/p/5599541.html
Copyright © 2011-2022 走看看