IOS - 本地数据持久化
转:相对复杂的App仅靠内存的数据肯定无法满足,数据写磁盘作持久化存储是几乎每个客户端软件都需要做的。简单如“是否第一次打开”的BOOL值,大 到游戏的进度和状态等数据,都需要进行本地持久化存储。这些数据的存储本质上就是写磁盘存文件,原始一点可以用iOS本身支持有 NSFileManager这样的API,或者干脆C语言fwrite/fread,Cocoa Touch本身也提供了一些存储方式,如NSUserDefaults,CoreData等。总的来说,iOS平台数据持久存储方法大致如下所列:
- Raw File APIs
- UserDefault
- NSCoding => NSKeyedArchived
- Plist File
- SQLite(使用C语言)
- CoreData
一、Raw File APIs
ObjC是C的一个超集,所以最笨的方法我们可以直接用C作文件读写来实现数据存储:
1. 写入文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// File path const char * pFilePath = [_path cStringUsingEncoding: NSUTF8StringEncoding ]; // Create a new file FILE * pFile = fopen(pFilePath, "w+" ); if (pFile == NULL ) { NSLog ( @"Open File ERROR!" ); return ; } const char * content = [_textField.text cStringUsingEncoding: NSUTF8StringEncoding ]; fwrite(content, sizeof (content), 1, pFile); fclose(pFile); |
2. 读取文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// File path const char * pFilePath = [_path cStringUsingEncoding: NSUTF8StringEncoding ]; // Create a new file FILE * pFile = fopen(pFilePath, "r+" ); if (pFile == NULL ) { NSLog ( @"Open File ERROR!" ); return ; } int fileSize = ftell(pFile); NSLog ( @"fileSize: %d" , fileSize); char * content[20]; fread(content, 20, 20, pFile); NSString * aStr = [ NSString stringWithFormat: @"%s" , &content]; if (aStr != nil && ![aStr isEqualToString: @"" ]) { _textField.text = aStr; } fclose(pFile); |
二、NSUserDefaults
但是既然在iOS平台作开发,我们当然不至于要到使用C的原生文件接口这种地步,下面就介绍几种iOS开发中常用的数据本地存储方式。使用起来最简
单的大概就是Cocoa提供的NSUserDefaults了,Cocoa会为每个app自动创建一个数据库,用来存储App本身的偏好设置,如:开关音
效,音量调整之类的少量信息。NSUserDefaults是一个单例,生命后期由App掌管,使用时用 [NSUserDefaults
standardUserDefaults]
接口获取单例对象。NSUserDefaults本质上是以Key-Value形式存成plist文件,放在App的
Library/Preferences目录下,对于已越狱的机器来说,这个文件是不安全的,所以**千万不要用NSUserDefaults来存储密码之类的敏感信息**,用户名密码应该使用**KeyChains**来存储。
1.写入数据
1
2
3
4
5
6
7
|
// 获取一个NSUserDefaults对象 NSUserDefaults * aUserDefaults = [ NSUserDefaults standardUserDefaults]; // 插入一个key-value值 [aUserDefaults setObject:_textField.text forKey: @"Text" ]; // 这里是为了把设置及时写入文件,防止由于崩溃等情况App内存信息丢失 [aUserDefaults synchronize]; |
2.读取数据
1
2
3
|
NSUserDefaults * aUserDefaults = [ NSUserDefaults standardUserDefaults]; // 获取一个key-value值 NSString * aStr = [aUserDefaults objectForKey: @"Text" ]; |
使用起来很简单吧,它的接口跟 NSMutableDictionary 一样,看它的头文件,事实上在内存里面也是用dictionary来存的。写数据的时候记得用 synchronize 方法写入文件,否则 crash了数据就丢了。
三、Plist
上一节提到NSUserDefaults事实上是存成Plist文件,只是Apple帮我们封装好了读写方法而已。NSUserDefaults的
缺陷是存储只能是Library/Preferences/<Application BundleIdentifier>.plist
这个文件,如果我们要自己写一个Plist文件呢?
使用NSFileManger可以很容易办到。事实上Plist文件是XML格式的,如果你存储的数据是Plist文件支持的类型,直接用
NSFileManager的writToFile接口就可以写入一个plist文件了。 ### Plist文件支持的数据格式有:
NSString, NSNumber, Boolean, NSDate, NSData, NSArray, 和NSDictionary.
其中,Boolean格式事实上以[NSNumber
numberOfBool:YES/NO];这样的形式表示。NSNumber支持float和int两种格式。
读写Plist文件
1. 首先创建plist文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 文件的路径 NSString * _path = [[ NSTemporaryDirectory () stringByAppendingString: @"save.plist" ] retain]; // 获取一个NSFileManger NSFileManager * aFileManager = [ NSFileManager defaultManager]; if (![aFileManager fileExistsAtPath:_path]){ // 文件不存在,创建之 NSMutableDictionary * aDefaultDict = [[ NSMutableDictionary alloc] init]; // 插入一个值,此时数据仍存在内存里 [aDefaultDict setObject: @"test" forKey: @"TestText" ]; // 使用NSMutableDictionary的写文件接口自动创建一个Plist文件 if (![aDefaultDict writeToFile:_path atomically: YES ]) { NSLog ( @"OMG!!!" ); } [aDefaultDict release]; } |
2. 写入文件
1
2
3
4
5
6
|
// 写入数据 NSMutableDictionary * aDataDict = [ NSMutableDictionary dictionaryWithContentsOfFile:_path]; [aDataDict setObject:_textField.text forKey: @"TestText" ]; if (![aDataDict writeToFile:_path atomically: YES ]) { NSLog ( @"OMG!!!" ); } |
3. 读取文件
1
2
3
4
5
|
NSMutableDictionary * aDataDict = [ NSMutableDictionary dictionaryWithContentsOfFile:_path]; NSString * aStr = [aDataDict objectForKey: @"TestText" ]; if (aStr != nil && aStr.length > 0) { _textField.text = aStr; } |
四、NSCoding + NSKeyedArchiver
上面介绍的几种方法中,直接用C语言的接口显然是最不方便的,拿出来的数据还得自己进行类型转换。NSUserDefaults和Plist文件支
持常用数据类型,但是不支持自定义的数据对象,好像Cocoa提供了NSCoding和NSKeyArchiver两个工具类,可以把我们自定义的对象编
码成二进制数据流,然后存进文件里面,下面的Sample为了简单我直接用cocoa的接口写成plist文件。
如果要使用这种方式进行存储,首先自定义的对象要继承NSCoding的delegate。
1
2
3
4
5
6
7
8
9
10
11
12
|
@interface WSNSCodingData : NSObject < NSCoding > 然后继承两个必须实现的方法encodeWithCoder:和initWithCoder: - ( void )encodeWithCoder:( NSCoder *)enoder { [enoder encodeObject:data forKey:kDATA_KEY]; } - ( id )initWithCoder:( NSCoder *)decoder { data = [[decoder decodeObjectForKey:kDATA_KEY] copy ]; return [ self init]; } |
这里data是我自己定义的WSNSCodingData这个数据对象的成员变量,由于数据在使用过程中需要持续保存在内存中,所以类型为
copy,或者retain也可以,记得在dealloc函数里面要realease。这样,我们就定义了一个可以使用NSCoding进行编码的数据对
象。
保存数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
- ( void )saveData { if (aData == nil ) { aData = [[WSNSCodingData alloc] init]; } aData.data = _textField.text; NSLog ( @"save data...%@" , aData.data); // 这里init的NSMutableData是临时用来存储数据的 NSMutableData * data = [[ NSMutableData alloc] init]; // 这个NSKeyedArchiver则是进行编码用的 NSKeyedArchiver * archiver = [[ NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [archiver encodeObject:aData forKey:DATA_KEY]; [archiver finishEncoding]; // 编码完成后的NSData,使用其写文件接口写入文件存起来 [data writeToFile:_path atomically: YES ]; [archiver release]; [data release]; NSLog ( @"save data: %@" , aData.data); } |
读取数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
- ( void )loadData { NSLog ( @"load file: %@" , _path); NSData * codedData = [[ NSData alloc] initWithContentsOfFile:_path]; if (codedData == nil ) return ; // NSKeyedUnarchiver用来解码 NSKeyedUnarchiver * unarchiver = [[ NSKeyedUnarchiver alloc] initForReadingWithData:codedData]; // 解码后的数据被存在一个WSNSCodingData数据对象里面 aData = [[unarchiver decodeObjectForKey:DATA_KEY] retain]; [unarchiver finishDecoding]; [unarchiver release]; [codedData release]; if (aData.data != nil ) { _textField.text = aData.data; } } |
所以其实使用NSCoding和NSKeyedArchiver事实上也是写plist文件,只不过对复杂对象进行了编码使得plist支持更多数据类型而已。
五、 SQLite
如果App涉及到的数据多且杂,还涉及关系查询,那么毋庸置疑要使用到数据库了。Cocoa本身提供了CoreData这样比较重的数据库框架,下
一节会讲到,这一节讲一个轻量级的数据库——SQLite。
SQLite是C写的的,做iOS开发只需要在工程里面加入需要的框架和头文件就可以用了,只是我们得用C语言来进行SQLite操作。
关于SQLite的使用参考了这篇文章:http://mobile.51cto.com/iphone-288898.htm但是稍微有点不一样。
1. 在编写SQLite代码之前,我们需要引入SQLite3头文件:
1
|
#import <sqlite3.h> |
2. 然后给工程加入 libsqlite3.0.dylib 框架。 3. 然后就可以开始使用了。首先是打开数据库:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
- ( void )openDB { NSArray * documentsPaths = NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ); NSString * databaseFilePath = [[documentsPaths objectAtIndex:0] stringByAppendingPathComponent: @"mydb" ]; // SQLite存的最终还是文件,如果没有该文件则会创建一个 if (sqlite3_open([databaseFilePath UTF8String], &_db) == SQLITE_OK) { NSLog ( @"Successfully open database." ); // 如果没有表则创建一个表 [ self creatTable]; } } |
3.关闭数据库,在dealloc函数里面调用:
1
2
3
|
- ( void )closeDB { sqlite3_close(_db); } |
4.创建一个表:
1
2
3
4
5
6
7
8
9
10
11
12
|
- ( void )creatTable { char * errorMsg; const char * createSql= "create table if not exists datas (id integer primary key autoincrement,name text)" ; if (sqlite3_exec(_db, createSql, NULL , NULL , &errorMsg) == SQLITE_OK) { NSLog ( @"Successfully create data table." ); } else { NSLog ( @"Error: %s" ,errorMsg); sqlite3_free(errorMsg); } } |
5. 写入数据库
1
2
3
4
5
6
7
8
9
10
|
- ( void )saveData { char * errorMsg; // 向 datas 表中插入 name = _textFiled.text 的数据 NSString * insertSQL = [ NSString stringWithFormat: @"insert into datas (name) values('%@')" , _textField.text]; // 执行该 SQL 语句 if (sqlite3_exec(_db, [insertSQL cStringUsingEncoding: NSUTF8StringEncoding ], NULL , NULL , &errorMsg)==SQLITE_OK) { NSLog ( @"insert ok." ); } } |
6. 读取数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
- ( void )loadData { [ self openDB]; const char * selectSql= "select id,name from datas" ; sqlite3_stmt * statement; if (sqlite3_prepare_v2(_db, selectSql, -1, &statement, nil )==SQLITE_OK) { NSLog ( @"select ok." ); } while (sqlite3_step(statement) == SQLITE_ROW) { int _id = sqlite3_column_int(statement, 0); NSString * name = [[ NSString alloc] initWithCString:( char *)sqlite3_column_text(statement, 1) encoding: NSUTF8StringEncoding ]; NSLog ( @"row>>id %i, name %@" ,_id,name); _textField.text = name; } sqlite3_finalize(statement); } |
五、CoreData
大型数据存储和管理。 XCode自带有图形化工具,可以自动生成数据类型的代码。 最终存储格式不一定存成SQLite,可以是XML等形式。
IOS - 本地数据持久化的更多相关文章
- iOS开发技术分享(1)— iOS本地数据存储
iOS开发技术分享(1)— iOS本地数据存储 前言: 我本是一名asp.net程序员,后来加入了iOS游戏开发队伍,到现在也有一年多的时间了.这一年来,每天都干到2.3点钟才睡觉,不为别的,只为了学 ...
- iOS开发——数据持久化&本地数据的存储(使用NSCoder将对象保存到.plist文件)
本地数据的存储(使用NSCoder将对象保存到.plist文件) 下面通过一个例子将联系人数据保存到沙盒的“documents”目录中.(联系人是一个数组集合,内部为自定义对象). 功能如下: ...
- iOS开发——数据持久化&使用NSUserDefaults来进行本地数据存储
使用NSUserDefaults来进行本地数据存储 NSUserDefaults适合存储轻量级的本地客户端数据,比如记住密码功能,要保存一个系统的用户名.密码.使用NSUserDefaults是首 ...
- iOS - OC 数据持久化
1.Sandbox 沙箱 iOS 为每个应用提供了独立的文件空间,一个应用只能直接访问为本应用分配的文件目录,不可以访问其他目录,每个应用自己独立的访问空间被称为该应用的沙盒.也就是说,一个应用与文件 ...
- iOS - Swift 数据持久化
1.Sandbox 沙箱 iOS 为每个应用提供了独立的文件空间,一个应用只能直接访问为本应用分配的文件目录,不可以访问其他目录,每个应用自己独立的访问空间被称为该应用的沙盒.也就是说,一个应用与文件 ...
- QF——iOS中数据持久化的几种方式
数据持久化的几种方式: 一.属性列表文件: .plist文件是种XML文件.数组,字典都可以和它互相转换.数组和字典可以写入本地变成plist文件.也可以读取本地plist文件,生成数组或字典. 读取 ...
- iOS中 数据持久化 UI高级_17
数据持久化的本质就是把数据由内写到本地(硬盘中),在iOS指将数据写到沙盒文件夹下: 沙盒机制:指的就是采用沙盒文件夹的形式管理应用程序的本地文件,而且沙盒文件夹的名字是随机分配的,采用十六进制方法命 ...
- iOS之数据持久化方案
概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据.在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) ...
- iOS的数据持久化
所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据.在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) pr ...
随机推荐
- 【Solr】索引库查询界面详解
目录 索引库查询界面详解 回到顶部 索引库查询界面详解 q:主查询条件.完全支持lucene语法.还进行了扩展. fq:过滤查询.是在主查询条件查询结果的基础上进行过滤.例如:product_pric ...
- PHP array_multisort() 函数详解 及 二维数组排序(模拟数据表记录按字段排序)
一.先看最简单的情况. 有两个数组: $arr1 = array(1, 9, 5); $arr2 = array(6, 2, 4); array_multisort($arr1, $arr2); pr ...
- servlet和http请求
1.servlet servlet是和平台无关的服务器组件,可以交互式的来浏览和修改数据,生成动态的web内容.它运行于 servlet容器中2.servlet容器 servlet容器负责servle ...
- JAVA操作ORACLE数据库的存储过程
一.任务提出 JAVA操作oracle11g存储过程实验需要完成以下几个实例: 1.调用没有返回参数的过程(插入记录.更新记录) 2.有返回参数的过程 3.返回列表的过程 4.返回带分页的列表的过程. ...
- Codeforces 271 Div 2 C. Captain Marmot
题目链接:http://codeforces.com/contest/474/problem/C 解题报告:给一个n,然后输入4*n个平面坐标系上的点,每四个点是一组,每个点有一个中心,这四个点可以分 ...
- BZOJ4435——[Cerc2015]Juice Junctions
0.题目大意:求两点之间的最小割之和 1.分析:很明显,最小割树,我们发现这个题并不能用n^3的方法来求答案.. 所以我们记录下所有的边,然后把边从大到小排序,然后跑一边类似kruskal的东西,顺便 ...
- 给UIView添加手势
对于不能addTarget的UI对象,添加手势为他们带来了“福音”,以为UIView添加手势为例,揭开手势的面目. 1,创建一个view先, UIView * jrView=[[UIViewalloc ...
- 500 OOPS: vsftpd: both local and anonymous access
配置ftp服务器,有如下报错 C:\netos74\bin>ftp 10.20.100.252Connected to 10.20.100.252.500 OOPS: vsftpd: both ...
- css选择器(E[att^=”val”]序号选择器)
一.基本选择器序号 选择器 含义1. * 通用元素选择器,匹配任何元素2. E 标签选择器,匹配所有使用E标签的元素3. .info class选择器,匹配所有class属性中包含info的元素4. ...
- 把ZenCart在线商店搭建到本地
前些日子,要给在线的zencart商店做一些小改动,需要安装一个插件.大家都知道,zencart有很多插件选用,兼容性也好坏不一,直接在正在运营的网站程序上修改,难免会影响到客户体验,出什么差错了代价 ...