您的位置:首页 > 其它

深入理解iPhone数据持久化(手把手教你iphone开发 – 基础篇)

2014-04-27 21:02 597 查看
在所有的移动开发平台数据持久化都是很重要的部分:在j2me中是rms或保存在应用程序的目录中,在symbian中可以保存在相应的磁盘目录中和数据库中。symbian中因为权限认证的原因,在3rd上大多数只能访问应用程序的private目录或其它系统共享目录。在iphone中,apple博采众长,提供了多种数据持久化的方法,下面笔者会逐个进行详细的讲解。

iphone提供的数据持久化的方法,从数据保存的方式上讲可以分为三大部分:属性列表、对象归档、嵌入式数据库(SQLite3)、其他方法。

、属性列表NSUserDefaults

NSUserDefaults类的使用和NSKeyedArchiver有很多类似之处,但是查看NSUserDefaults的定义可以看出,NSUserDefaults直接继承自NSObject而NSKeyedArchiver 继承自NSCoder。这意味着NSKeyedArchiver实际上是个归档持久化的类,也就可以使用NSCoder类的[encodeObject: (id)objv forKey:(NSString *)key]方法来对数据进行持久化存储。

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
26

- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSString *strOne = @"Persistent data1";
NSString *strTwo = @"Persistent data 2";

NSMutableArray *persistentArray = [[NSMutableArray alloc] init];
[persistentArray addObject:strOne];
[persistentArray addObject:strTwo];

//archive
NSUserDefaults *persistentDefaults = [NSUserDefaults standardUserDefaults];
[persistentDefaults setObject:persistentArray forKey:@"myDefault"];
NSString *descriptionDefault = [persistentDefaults description];
NSLog(@"NSUserDefaults description is :%@",descriptionDefault);

//unarchive
NSArray *UnpersistentArray =
[persistentDefaults objectForKey:@"myDefault"];

NSString *UnstrOne = [UnpersistentArray objectAtIndex:0];
NSString *UnstrTwo = [UnpersistentArray objectAtIndex:1];

NSLog(@"UnstrOne = %@,UnstrTwo = %@",UnstrOne,UnstrTwo);

// Override point for customization after application launch
[window makeKeyAndVisible];
}

二、对象归档NSKeyedArchiverNSKeyedUnarchiver

iPhone和symbian 3rd一样,会为每一个应用程序生成一个私有目录,这个目录位于

/Users/sundfsun2009/Library/Application Support/iPhone Simulator/User/Applications下,并随即生成一个数字字母串作为目录名,在每一次应用程序启动时,这个字母数字串都是不同于上一次的,上一次的应用程序目录信息被转换成名为.DS_Store隐藏文件,这个目录的文件结构如下图:
通常使用Documents目录进行数据持久化的保存,而这个Documents目录可以通过NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserdomainMask,YES)得到,代码如下:

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
2627
28
29
30
31

- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSString *strOne = @"Persistent data1";
NSString *strTwo = @"Persistent data 2";

NSArray *persistentArray = [NSArray arrayWithObjects:strOne,strTwo,nil];
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,    NSAllDomainsMask, YES);

int pathLen = [pathArray count];

NSLog(@"path number is :%d",pathLen);

NSString *filePath;

for(int i = 0; i < pathLen; i++)
{
filePath = [pathArray objectAtIndex:i];
NSLog(@"%d path is :%@",i,filePath);
}

NSString *myFilename = [filePath stringByAppendingPathComponent:@"myFile.rtf"];

NSLog(@"myfile's path is :%@",myFilename);

// no files generated in correspond directory now

[NSKeyedArchiver archiveRootObject:persistentArray toFile:myFilename];
// now the myFile.rtf is generated

// Override point for customization after application launch
[window makeKeyAndVisible];
}

NSSearchPathForDirectoriesInDomains()的第二个参数是个枚举值,在笔者的测试代码中,只有NSUserDomainMask和NSAllDomainsMask可以获取到目录数为1,其余的皆为0,打印出来的结果如下:


[Session started at 2009-11-10 21:30:08 +0800.]
2009-11-10 21:30:10.516 PersistentExample[763:207] path number is :1
2009-11-10 21:30:10.518 PersistentExample[763:207] 0 path is :/Users/sundfsun2009/Library/Application Support/iPhone Simulator/User/Applications/C93DC783-F137-4660-AE5A-08C3E11C774B/Documents
2009-11-10 21:30:10.521 PersistentExample[763:207] myfile’s path is :/Users/sundfsun2009/Library/Application Support/iPhone Simulator/User/Applications/C93DC783-F137-4660-AE5A-08C3E11C774B/Documents/myFile.rtf
Terminating in response to SpringBoard’s termination.

[Session started at 2009-11-10 21:32:27 +0800.]
2009-11-10 21:32:30.091 PersistentExample[803:207] path number is :1
2009-11-10 21:32:30.092 PersistentExample[803:207] 0 path is :/Users/sundfsun2009/Library/Application Support/iPhone Simulator/User/Applications/763E6772-E754-452F-8532-80C2CE4466B5/Documents
2009-11-10 21:32:30.100 PersistentExample[803:207] myfile’s path is :/Users/sundfsun2009/Library/Application Support/iPhone Simulator/User/Applications/763E6772-E754-452F-8532-80C2CE4466B5/Documents/myFile.rtf
Terminating in response to SpringBoard’s termination.


从打印的结果如下,每次应用程序启动时生成的数字字母串目录名字并不一样。在调用[NSKeyedArchiver archiveRootObject:persistentArray toFile:myFilename]方法前,文件myFile.rtf并每生成,只有在调用此方法后才产生相应的文件。

下面需要把数据从属性列表中读取出来,在上面的代码中,笔者使用NSArray保存数据。但在大多数应用程序中,数据的尺寸并不是固定的,这个时候就需要使用NSMutalbeArray动态的保存数据,代码优化如下:

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
2627
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

(void)applicationDidFinishLaunching:(UIApplication *)application {
NSString *myFilename;
// archive
{
NSString *strOne = @"Persistent data1";
NSString *strTwo = @"Persistent data 2";

NSMutableArray *persistentArray = [[NSMutableArray alloc] init];
[persistentArray addObject:strOne];
[persistentArray addObject:strTwo];

NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,    NSAllDomainsMask, YES);

int pathLen = [pathArray count];
NSLog(@"path number is :%d",pathLen);

NSString *filePath;

for(int i = 0; i < pathLen; i++)
{
filePath = [pathArray objectAtIndex:i];

NSLog(@"%d path is :%@",i,filePath);
}

myFilename = [filePath stringByAppendingPathComponent:@"myFile.rtf"];

NSLog(@"myfile's path is :%@",myFilename);

[NSKeyedArchiver archiveRootObject:persistentArray toFile:myFilename];
}

// unarchive
{
NSArray *unarchiveArray = [NSKeyedUnarchiver unarchiveObjectWithFile:myFilename];
NSString *UnstrOne = [unarchiveArray objectAtIndex:0];
NSString *UnstrTwo = [unarchiveArray objectAtIndex:1];

NSLog(@"UnstrOne = %@,UnstrTwo = %@",UnstrOne,UnstrTwo);
}

// Override point for customization after application launch
[window makeKeyAndVisible];
}

输出结果如下:


[Session started at 2009-11-10 22:41:57 +0800.]
2009-11-10 22:41:59.344 PersistentExample[1082:207] path number is :1
2009-11-10 22:41:59.346 PersistentExample[1082:207] 0 path is :/Users/sundfsun2009/Library/Application Support/iPhone Simulator/User/Applications/055CD17C-864E-4A83-ABF0-5F01EE85BD5A/Documents
2009-11-10 22:41:59.355 PersistentExample[1082:207] myfile’s path is :/Users/sundfsun2009/Library/Application Support/iPhone Simulator/User/Applications/055CD17C-864E-4A83-ABF0-5F01EE85BD5A/Documents/myFile.rtf
2009-11-10 22:41:59.357 PersistentExample[1082:207] UnstrOne = Persistent data1,UnstrTwo = Persistent data 2
Terminating in response to SpringBoard’s termination.


从上面的图中可以看到,目录中还有个tmp目录,读者也可以把数据保存在tmp目录中,获取这个目录使用NSTemporaryDirectory()方法。
三、嵌入式数据库(SQLite3)

嵌入式数据库持久化数据就是把数据保存在iphone的嵌入式数据库系统SQLite3中,本质上来说,数据库持久化操作是基于文件持久化基础之上的。
要使用嵌入式数据库SQLite3,首先需要加载其动态库libsqlite3.dylib,这个文件位于/Xcode3.1.4/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.sdk/usr/lib目录下。在Framework文件夹上右击,选择“Adding->Existing Files…”,定位到上述目录并加载到文件夹。

首先在头文件中做如下修改:

1
2
3
4
5
6
7
8
9
10
11
12
13

#import <UIKit/UIKit.h>

#include "sqlite3.h"
#define kFileName @"mydb.sql"

@interface PersistentExampleAppDelegate : NSObject <UIApplicationDelegate> {
sqlite3 *database;
UIWindow *window;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

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
2627
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

(void)applicationDidFinishLaunching:(UIApplication *)application {

NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *paths = [[path objectAtIndex:0] stringByAppendingPathComponent:kFileName];

NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL findFile = [fileManager fileExistsAtPath:paths];

NSLog(@"Database file path = %@",paths);

// 如果找到了数据库文件
if(findFile)
{
NSLog(@"Database file have already existed.");

if(sqlite3_open([paths UTF8String], &database) != SQLITE_OK)//打开数据库失败
{
sqlite3_close(database);
NSAssert(0,@"Failed to open database");
}
}else
{
NSLog(@"Database file does not exsit!");
if(sqlite3_open([paths UTF8String], &database) != SQLITE_OK)//打开数据库失败
{
sqlite3_close(database);
NSAssert(0,@"Failed to open database");
}
}

char *errorMsg;

//创建表
NSString *createSQL = @"create table if not exists fields (row integer primary key, field_data text);";
if(sqlite3_exec(database, [createSQL UTF8String],NULL,NULL,&errorMsg)!=SQLITE_OK)
{
sqlite3_close(database);
NSAssert1(0,@"Error creating table: %s",errorMsg);
}

NSString *strOne = @"Persistent data1";
NSString *strTwo = @"Persistent data 2";

NSMutableArray *persistentArray = [[NSMutableArray alloc] init];
[persistentArray addObject:strOne];
[persistentArray addObject:strTwo];

for (int i = 0; i < [persistentArray count]; i++) {
NSString *upDataSQL = [[NSString alloc] initWithFormat:@"insert or replace into
fields (row,field_data) values (%d,'%@');",i,[persistentArray objectAtIndex:i]];

char* errorMsg;
if(sqlite3_exec(database,[upDataSQL UTF8String],NULL,NULL,&errorMsg)
!= SQLITE_OK)
{
sqlite3_close(database);
NSAssert(0,@"Failed to open database");
}
}

//unarchive
NSString *query = @"select row, field_data from fields order by row";//查找表中的数据
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)
== SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
int row = sqlite3_column_int(statement, 0);
char *rowData = (char *)sqlite3_column_text(statement, 1);

NSString *fieldName = [[NSString alloc] initWithFormat:@"show%d",row];
NSString *fieldValue = [[NSString alloc] initWithUTF8String:rowData];

NSLog(@"fieldName is :%@,fieldValue is :%@",fieldName,fieldValue);

[fieldName release];
[fieldValue release];
}
sqlite3_finalize(statement);
}

// Override point for customization after application launch
[window makeKeyAndVisible];
}

在上面的代码中,我们使用
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL findFile = [fileManager fileExistsAtPath:paths];
来判断数据库文件是否已经存在,其实在大多数情况下是没有必要的,sqlite3_open()方法会自动帮我们判断数据库文件是否存在,如果不存在则创建心的数据库文件。
四、其它方法

除了上面的三种方法来保存持久化数据以外,我们还可以用写文件到磁盘的方式来保存持久化数据。

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
2627
28
29
30
31
32
33

(void)applicationDidFinishLaunching:(UIApplication *)application {

NSString *strOne = @"Persistent data1";
NSString *strTwo = @"Persistent data 2";

NSMutableArray *persistentArray = [[NSMutableArray alloc] init];
[persistentArray addObject:strOne];
[persistentArray addObject:strTwo];

NSArray *filePathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath =
-          [[filePathArray objectAtIndex:0] stringByAppendingPathComponent:@"mydatas.plist"];

[[NSArray arrayWithObjects:persistentArray,nil] writeToFile:filePath atomically:NO];

//load
NSMutableArray *saveDataArray = [[NSMutableArray alloc] init];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
saveDataArray = [NSMutableArray arrayWithContentsOfFile:filePath];
else
saveDataArray = [NSMutableArray arrayWithContentsOfFile:[[NSBundle
-                  mainBundle] pathForResource:@"Savedatas" ofType:@"plist"]];
-
NSArray *strArray = [saveDataArray objectAtIndex:0];

NSString *UnstrOne = [strArray objectAtIndex:0];
NSString *UnstrTwo = [strArray objectAtIndex:1];

// Override point for customization after application launch
[window makeKeyAndVisible];
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: