iOS基础 - 数据存取

1、iOS应用数据存储的经常使用方式

XML属性列表(plist)归档sql

Preference(偏好设置)数据库

NSKeyedArchiver归档缓存

SQLite3 app

Core Data框架

2、应用沙盒

每一个iOS应用都有本身的应用沙盒(应用沙盒就是文件系统目录),与其余文件系统隔离。应用必须待在本身的沙盒里,其余应用不能访问该沙盒函数

应用沙盒的文件系统目录,以下图所示(假设应用的名称叫Layer字体

模拟器应用沙盒的根路径在: (apple是用户名, 6.0是模拟器版本)编码

/Users/apple/Library/Application Support/iPhone Simulator/6.0/Applicationsatom

3、应用沙盒结构分析

应用程序包:(上图中的Layer)包含了全部的资源文件和可执行文件url

Documents:保存应用运行时生成的须要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录

tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录

Library/Caches:保存应用运行时生成的须要持久化的数据,iTunes同步设备时不会备份该目录。通常存储体积大、不须要备份的非重要数据

Library/Preference:保存应用的全部偏好设置,iOSSettings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录

4、应用沙盒目录的常见获取方式

沙盒根目录:NSString *home = NSHomeDirectory();

Documents(2种方式)

利用沙盒根目录拼接”Documents”字符串

NSString *home = NSHomeDirectory();

NSString *documents = [home stringByAppendingPathComponent:@"Documents"];

// 不建议采用,由于新版本的操做系统可能会修改目录名

利用NSSearchPathForDirectoriesInDomains函数

// NSUserDomainMask 表明从用户文件夹下找

// YES 表明展开路径中的波浪字符“~

NSArray *array =  NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);

// iOS中,只有一个目录跟传入的参数匹配,因此这个集合里面只有一个元素

NSString *documents = [array objectAtIndex:0];

tmpNSString *tmp = NSTemporaryDirectory();

Library/Caches(Documents相似的2种方法)

利用沙盒根目录拼接”Caches”字符串

利用NSSearchPathForDirectoriesInDomains函数(将函数的第2个参数改成:NSCachesDirectory便可)

Library/Preference:经过NSUserDefaults类存取该目录下的设置信息

5、属性列表

属性列表是一种XML格式的文件,拓展名为plist

若是对象是NSStringNSDictionaryNSArrayNSDataNSNumber等类型,就可使用writeToFile:atomically:方法直接将对象写到属性列表文件中

6、属性列表-归档NSDictionary

将一个NSDictionary对象归档到一个plist属性列表中

// 将数据封装成字典

NSMutableDictionary *dict = [NSMutableDictionary dictionary];

[dict setObject:@"母鸡" forKey:@"name"];

[dict setObject:@"15013141314" forKey:@"phone"];

[dict setObject:@"27" forKey:@"age"];

// 将字典持久化到Documents/stu.plist文件中

[dict writeToFile:path atomically:YES];

7、属性列表-恢复NSDictionary

读取属性列表,恢复NSDictionary对象

// 读取Documents/stu.plist的内容,实例化NSDictionary

NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];

NSLog(@"name:%@", [dict objectForKey:@"name"]);

NSLog(@"phone:%@", [dict objectForKey:@"phone"]);

NSLog(@"age:%@", [dict objectForKey:@"age"]);

8、偏好设置

不少iOS应用都支持偏好设置,好比保存用户名、密码、字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能

每一个应用都有个NSUserDefaults实例,经过它来存取偏好设置

好比,保存用户名、字体大小、是否自动登陆

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

[defaults setObject:@"itcast" forKey:@"username"];

[defaults setFloat:18.0f forKey:@"text_size"];

[defaults setBool:YES forKey:@"auto_login"];

读取上次保存的设置

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

NSString *username = [defaults stringForKey:@"username"];

float textSize = [defaults floatForKey:@"text_size"];

BOOL autoLogin = [defaults boolForKey:@"auto_login"];

注意:UserDefaults设置数据时,不是当即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。因此调用了set方法以后数据有可能尚未写入磁盘应用程序就终止了。出现以上问题,能够经过调用synchornize方法强制写入

[defaults synchornize];

9、NSKeyedArchiver

若是对象是NSStringNSDictionaryNSArrayNSDataNSNumber等类型,能够直接用NSKeyedArchiver进行归档和恢复

不是全部的对象均可以直接用这种方法进行归档,只有遵照了NSCoding协议的对象才能够

NSCoding协议有2个方法:

encodeWithCoder:

每次归档对象时,都会调用这个方法。通常在这个方法里面指定如何归档对象中的每一个实例变量,可使用encodeObject:forKey:方法归档实例变量

initWithCoder:

每次从文件中恢复(解码)对象时,都会调用这个方法。通常在这个方法里面指定如何解码文件中的数据为对象的实例变量,可使用decodeObject:forKey方法解码实例变量

10、NSKeyedArchiver-归档NSArray

归档一个NSArray对象到Documents/array.archive

NSArray *array = [NSArray arrayWithObjects:@a,@b,nil];

[NSKeyedArchiver archiveRootObject:array toFile:path];

恢复(解码)NSArray对象

NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

11、NSKeyedArchiver-归档Person对象(Person.h

@interface Person : NSObject<NSCoding>

@property (nonatomic, copy) NSString *name;

@property (nonatomic, assign) int age;

@property (nonatomic, assign) float height;

@end

12、NSKeyedArchiver-归档Person对象(Person.m

@implementation Person

- (void)encodeWithCoder:(NSCoder *)encoder {

    [encoder encodeObject:self.name forKey:@"name"];

    [encoder encodeInt:self.age forKey:@"age"];

    [encoder encodeFloat:self.height forKey:@"height"];

}

- (id)initWithCoder:(NSCoder *)decoder {

    self.name = [decoder decodeObjectForKey:@"name"];

    self.age = [decoder decodeIntForKey:@"age"];

    self.height = [decoder decodeFloatForKey:@"height"];

    return self;

}

- (void)dealloc {

    [super dealloc];

    [_name release];

}

@end

十3、NSKeyedArchiver-归档Person对象(编码和解码)

归档(编码)

Person *person = [[[Person alloc] init] autorelease];

person.name = @"Monica";

person.age = 27;

person.height = 1.83f;

[NSKeyedArchiver archiveRootObject:person toFile:path];

恢复(解码)

Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

十4、NSKeyedArchiver-归档对象的注意

若是父类也遵照了NSCoding协议,请注意:

应该在encodeWithCoder:方法中加上一句

[super encodeWithCode:encode];

确保继承的实例变量也能被编码,即也能被归档

应该在initWithCoder:方法中加上一句

self = [super initWithCoder:decoder];

确保继承的实例变量也能被解码,即也能被恢复

十5、NSData

使用archiveRootObject:toFile:方法能够将一个对象直接写入到一个文件中,但有时候可能想将多个对象写入到同一个文件中,那么就要使用NSData来进行归档对象

NSData能够为一些数据提供临时存储空间,以便随后写入文件,或者存放从磁盘读取的文件内容。可使用[NSMutableData data]建立可变数据空间

十6、NSData-归档2Person对象到同一文件中

归档(编码)

// 新建一块可变数据区

NSMutableData *data = [NSMutableData data];

// 将数据区链接到一个NSKeyedArchiver对象

NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];

// 开始存档对象,存档的数据都会存储到NSMutableData

[archiver encodeObject:person1 forKey:@"person1"];

[archiver encodeObject:person2 forKey:@"person2"];

// 存档完毕(必定要调用这个方法)

[archiver finishEncoding];

// 将存档的数据写入文件

[data writeToFile:path atomically:YES];

十7、NSData-从同一文件中恢复2Person对象

恢复(解码)

// 从文件中读取数据

NSData *data = [NSData dataWithContentsOfFile:path];

// 根据数据,解析成一个NSKeyedUnarchiver对象

NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

Person *person1 = [unarchiver decodeObjectForKey:@"person1"];

Person *person2 = [unarchiver decodeObjectForKey:@"person2"];

// 恢复完毕

[unarchiver finishDecoding];

十8、利用归档实现深复制

好比对一个Person对象进行深复制

// 临时存储person1的数据

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1];

// 解析data,生成一个新的Person对象

Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];

// 分别打印内存地址

NSLog(@"person1:0x%x", person1); // person1:0x7177a60

NSLog(@"person2:0x%x", person2); // person2:0x7177cf0

十9、SQLite3

SQLite3是一款开源的嵌入式关系型数据库,可移植性好、易使用、内存开销小

SQLite3是无类型的,意味着你能够保存任何类型的数据到任意表的任意字段中。好比下列的创表语句是合法的:

create table t_person(name, age);

为了保证可读性,建议仍是把字段类型加上:

create table t_person(name text, age integer);

SQLite3经常使用的5种数据类型:textintegerfloatbooleanblob

iOS中使用SQLite3,首先要添加库文件libsqlite3.dylib和导入主头文件

二10、建立、打开、关闭数据库

建立或打开数据库

// path为:~/Documents/person.db

sqlite3 *db;

int result = sqlite3_open([path UTF8String], &db); 

代码解析:

sqlite3_open()将根据文件路径打开数据库,若是不存在,则会建立一个新的数据库。若是result等于常量SQLITE_OK,则表示成功打开数据库

sqlite3 *db:一个打开的数据库实例

数据库文件的路径必须以C字符串(而非NSString)传入

关闭数据库:sqlite3_close(db);

二11、执行不返回数据的SQL语句

执行创表语句

char *errorMsg;  // 用来存储错误信息

char *sql = "create table if not exists t_person(id integer primary key autoincrement, name text, age integer);";

int result = sqlite3_exec(db, sql, NULL, NULL, &errorMsg);

代码解析:

sqlite3_exec()能够执行任何SQL语句,好比创表、更新、插入和删除操做。可是通常不用它执行查询语句,由于它不会返回查询到的数据

sqlite3_exec()还能够执行的语句:

开启事务:begin transaction;

回滚事务:rollback;

提交事务:commit;

二12、带占位符插入数据

char *sql = "insert into t_person(name, age) values(?, ?);";

sqlite3_stmt *stmt;

if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {

    sqlite3_bind_text(stmt, 1, "母鸡", -1, NULL);

    sqlite3_bind_int(stmt, 2, 27);

}

if (sqlite3_step(stmt) != SQLITE_DONE) {

    NSLog(@"插入数据错误");

}

sqlite3_finalize(stmt);

代码解析:

sqlite3_prepare_v2()返回值等于SQLITE_OK,说明SQL语句已经准备成功,没有语法问题

sqlite3_bind_text():大部分绑定函数都只有3个参数

1个参数是sqlite3_stmt *类型

2个参数指占位符的位置,第一个占位符的位置是1,不是0

3个参数指占位符要绑定的值

4个参数指在第3个参数中所传递数据的长度,对于C字符串,能够传递-1代替字符串的长度

5个参数是一个可选的函数回调,通常用于在语句执行后完成内存清理工做

sqlite_step():执行SQL语句,返回SQLITE_DONE表明成功执行完毕

sqlite_finalize():销毁sqlite3_stmt *对象

二十3、查询数据

char *sql = "select id,name,age from t_person;";

sqlite3_stmt *stmt;

if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {

    while (sqlite3_step(stmt) == SQLITE_ROW) {

        int _id = sqlite3_column_int(stmt, 0);

        char *_name = (char *)sqlite3_column_text(stmt, 1);

        NSString *name = [NSString stringWithUTF8String:_name];

        int _age = sqlite3_column_int(stmt, 2);

        NSLog(@"id=%i, name=%@, age=%i", _id, name, _age);

    }

}

sqlite3_finalize(stmt);

代码解析

sqlite3_step()返回SQLITE_ROW表明遍历到一条新记录

sqlite3_column_*()用于获取每一个字段对应的值,第2个参数是字段的索引,从0开始

二十4、Core Data

Core Data框架提供了对象-关系映射(ORM)的功能,即可以将OC对象转化成数据,保存在SQLite3数据库文件中,也可以将保存在数据库中的数据还原成OC对象。在此数据操做期间,不须要编写任何SQL语句。使用此功能,要添加CoreData.framework和导入主头文件<CoreData/CoreData.h>

二十5、模型文件

Core Data,须要进行映射的对象称为实体(entity),并且须要使用Core Data的模型文件来描述应用的全部实体和实体属性

这里以PersonCard(身份证)2个实体为例子,先看看实体属性和之间的关联关系

Person中有个Card属性,Card中有个Person属性

属于一对一双向关联

二十6、NSManagedObject

经过Core Data从数据库取出的对象,默认状况下都是NSManagedObject对象

NSManagedObject的工做模式有点相似于NSDictionary对象,经过键-值对来存取全部的实体属性

setValue:forKey: 存储属性值(属性名为key)

valueForKey: 获取属性值(属性名为key)

二十7、Core Data主要对象

NSManagedObjectContext

用途:负责应用和数据库之间的交互(CRUD)

NSPersistentStoreCoordinator

用途:添加持久化存储库(好比SQLite数据库)

NSManagedObjectModel

用途:表明Core Data的模型文件

NSEntityDescription

用途:用来描述实体

NSEntityDescription

用途:用来描述实体

二十8、搭建Core Data上下文环境

从应用程序包中加载模型文件

NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];

传入模型,初始化NSPersistentStoreCoordinator

NSPersistentStoreCoordinator *psc = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model] autorelease];

构建SQLite文件路径

NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSURL *url = [NSURL fileURLWithPath:[docs stringByAppendingPathComponent:@"person.data"]];

添加持久化存储库,这里使用SQLite做为存储库

NSError *error = nil;

NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];

if (store == nil) { // 直接抛异常

    [NSException raise:@"添加数据库错误" format:@"%@", [error localizedDescription]];

}

初始化上下文,设置persistentStoreCoordinator属性

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];

context.persistentStoreCoordinator = psc;

// 用完以后,仍是要[context release];

二十9、添加数据

传入上下文,建立一个Person实体对象

NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];

设置简单属性

[person setValue:@"Monica" forKey:@"name"];

[person setValue:[NSNumber numberWithInt:27] forKey:@"age"];

传入上下文,建立一个Card实体对象

NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];

[card setValue:@"4414241933432" forKey:@"no"];

设置PersonCard之间的关联关系

[person setValue:card forKey:@"card"];

利用上下文对象,将数据同步到持久化存储库

NSError *error = nil;

BOOL success = [context save:&error];

if (!success) {

    [NSException raise:@"访问数据库错误" format:@"%@", [error localizedDescription]];

}

// 若是是想作更新操做:只要在更改了实体对象的属性后调用[context save:&error],就能将更改的数据同步到数据库

三10、查询数据

初始化一个查询请求

NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];

设置要查询的实体

NSEntityDescription *desc = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];

设置排序(按照age降序)

NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];

request.sortDescriptors = [NSArray arrayWithObject:sort];

设置条件过滤(name like '%Itcast-1%')

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*Itcast-1*"];

request.predicate = predicate;

执行请求

NSError *error = nil;

NSArray *objs = [context executeFetchRequest:request error:&error];

if (error) {

    [NSException raise:@"查询错误" format:@"%@", [error localizedDescription]];

}

遍历数据

for (NSManagedObject *obj in objs) {

    NSLog(@"name=%@", [obj valueForKey:@"name"]

}

三11、删除数据

传入须要删除的实体对象

[context deleteObject:managedObject];

将结果同步到数据库

NSError *error = nil;

[context save:&error];

if (error) {

    [NSException raise:@"删除错误" format:@"%@", [error localizedDescription]];

}

三12、Core Data的延迟加载

Core Data不会根据实体中的关联关系当即获取相应的关联对象

好比经过Core Data取出Person实体时,并不会当即查询相关联的Card实体;当应用真的须要使用Card时,才会查询数据库,加载Card实体的信息

三十3、建立NSManagedObject的子类

默认状况下,利用Core Data取出的实体都是NSManagedObject类型的,可以利用键-值对来存取数据

可是通常状况下,实体在存取数据的基础上,有时还须要添加一些业务方法来完成一些其余任务,那么就必须建立NSManagedObject的子类

那么生成一个Person实体对象就应该这样写

Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];

person.name = @"Monica";

person.age = [NSNumber numberWithInt:27];

Card *card = [NSEntityDescription insertNewObjectForEntityForName:@Card" inManagedObjectContext:context];

card.no = @4414245465656";

person.card = card;

相关文章
相关标签/搜索