Sandbox(沙盒机制)
iOS中得沙盒机制(sandbox)是一种安全体系,它规定了应用程序只能在为该应用程序创建的文件夹内读取文件,不可以访问其他地方的内容。所有的非代码文件都保存在这个地方,如图片,声音,属性列表和文本文件等。
- 每个应用程序都在自己的沙盒内
- 不能随意跨越自己的沙盒去访问别的应用程序的沙盒内容
应用程序向外请求或接受数据都需要经过权限认证
一个沙盒中包含四部分
- .app文件,即可运行的应用文件;
- Document,苹果建议将程序创建或程序浏览的文件数据保存在该目录下,iTunes备份和恢复时会包括该目录;
- Library,存储程序的默认设置或其它状态信息;
- Library/Caches,存放缓存文件,iTunes不会备份此目录,此目录下的文件不会在应用退出删除;
- tmp,创建和存放临时文件的地方,iTu不会备份此目录。
代码获取沙盒路径的方法
获取根目录
NSString *homePath = NSHomeDirectory();
获取Document目录
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)[0];获取Cache目录
NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask, YES)[0];获取Library目录
NSString *libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES)[0];- 获取tmp目录
NSString *tmpPath = NSTemporaryDirectory();
iOS数据持久化技术
数据持久化既是,能将内存中的数据模型
转换为存储模型
,并能在将来需要时将存储模型
还原为数据模型
的一种机制。
说明 : 通俗的讲,也就是将数据保存在非易失性的设备中,并且能在需要时恢复。针对苹果设备来说,就是从闪存到内存的过程。
iOS开发中数据持久化的方法
- Row File APIs(C语言的文件操作,iOS的NSFilemanager)
- NSUserDefaults (默认保存文件在对应的程序包sandbox的目录下的library/Preferences)
- Plist(属性列表)
- NSCoding + Archiver&Unarchiver (对象归档)
- SQLite (数据库)
- FMDB (对SQLite的封装)
@property (nonatomic, strong) NSString *filePath;
@property (nonatomic, strong) UITextField *textField;
#define kFileName @”test.txt”
ROW APIs
C语言文件操作### ROW APIs
- 创建文件路径
|
|
- 文件的写入
|
|
- 文件的读取
|
|
OC NSFileManager文件管理器操作
- 创建文件路径
|
|
- 文件的写入
|
|
- 文件的读取
|
|
NSUserDefaults
>
- 直接使用原始的文件操作API,不管是C语言的还是OC的都不太方便
Cocoa会为每个app自动创建一个数据库,用来存储App本身的偏好设置,如:开关
值,音量值之类的少量信息NSUserDefaults使用时用 [NSUserDefaults standardUserDefaults] 接口获取单例对象
- NSUserDefaults本质上是以Key-Value形式存成plist文件,放在App的 Library/Preferences目录下
- 这个文件是
不安全
的,所以千万不要用NSUserDefaults来存储密码之类的敏感信息,用户名和密码应该使用KeyChains来存储
- 文件的写入
|
|
- 文件的读取
|
|
**说明:** *对NSUserDefaults单例对象的操作,实质上还是对PList文件 (Library/Preferences/<Application BundleIdentifier>.plist)的读写,只是Apple帮我们封装好了 读写方法。*
Plist
>
- NSUserDefaults只能读写Library/Preferences/
.plist这个 文件 - PList文件是XML格式的,只能存放固定数据格式的对象
- PList文件支持的数据格式有NSString, NSNumber, Boolean, NSDate, NSData, NSArray,和NSDictionary。其中,Boolean格式事实上以[NSNumber numberOfBool:YES/NO];这样的形式表示。NSNumber支持float和int两种格式。
- 创建文件路径
|
|
- 写入plist文件
|
|
- plist文件的读取
|
|
Archiver&Unarchiver
>
- NSUserDefaults和Plist文件支持常用数据类型,但是不支持自定义的数据对象
- Cocoa提供了NSCoding和NSKeyArchiver两个工具类,可以把我们自定义的对象编码 成二进制数据流,然后存进文件里面
NSCoding协议
|
|
|
|
----
- 保存数据
|
|
- 读取数据
|
|
SQLite
SQLite shell command
SQLite usage
创建数据库连接对象: sqlite3
创建预编译语句对象:sqlite3_stmt
打开数据
sqlite3_open()
将SQL语句转换为预编译语句对象
sqlite3_prepare_v2()
执行预编译语句,每次处理一次,不需要返回值的语句(如INSERT,UPDATE,DELETE),只需要执行该函数即可
sqlite3_step()
获取数据库中得不同类型的值
- sqlite3_column_blob()
- sqlite3_column_bytes()
- sqlite3_column_bytes16()
- sqlite3_column_count()
- sqlite3_column_double()
- sqlite3_column_int()
- sqlite3_column_int64()
- sqlite3_column_text()
- sqlite3_column_text16()
- sqlite3_column_type()
- sqlite3_column_value()
销毁有sqlite3_prepare_v2()函数创建的预处理语句对象
sqlite3_finalize()
关闭数据库(即销毁数据库连接对象)
sqlite3_close()
FMDB
>
FMDB数据库操作类对sqlite3的操作进行了便利的封装并保证了多线程下的安全地操作数据库
FEMDB有三个主要的类
- FMDatabase - 表示一分单独的SQLite数据库,用来执行SQLite的命令
- FMResultSet - 表示FMDatabase执行查询结果集
- FMDatabaseQueue - 在多线程中执行多个查询或更新使用该类是线程安全的
数据库的创建
#define kDBFileName @”database.sqlite”
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)[0];
NSString *DBPath = [docPath stringByAppendingPathComponent:kDBFileName];
FMDatabase *database = [FMDatabase databaseWithPath:DBPath];
打开数据库
if(![database open]){
NSLog(@"Open database failed !");
return;
}
执行更新
- executeUpdate
一切不是SELECT命令的命令都是为更新。包括CREATE,UPDATE,INSERT,ALTER等。
执行结果返回一个BOOL值。YES表示成功,NO表示失败。可以调用 -lastErrorMessage和 -lastErrorCode方法获取更多的信息。
执行查询
- executeQuery
执行结果返回FMResultSet对象,失败返回nil。同样可以调用-lastErrorMessage和 -lastErrorCode方法获取更多的信息。获得的FMResultSet对象rs后,既是只有一条记录,一样使用[rs next];
eg: FMResultSet *rs = [db executeQuery:@"SELECT Name, Age, FROM PersonList"];
while ([rs next]) {
NSString *name = [rs stringForColumn:@"Name"];
int age = [rs intForColumn:@"Age"];
}
FMResultSet根据类型提取数据
- objectForColumnName:
- longForColumn:
- nlongLongIntForColumn:
- boolForColumn:
- doubleForColumn:
- stringForColumn:
- dateForColumn:
- dataForColumn:
- dataNoCopyForColumn:
- UTF8StringForColumnName:

以上方法,都有个{type}ForColumnIndex:版本,根据column的位置提取数据
有些时候,只是需要query某一个row里特定的一个数值(比如只是要找John的年龄),FMDB 提供了几个比较简便的方法。这些方法定义在FMDatabaseAdditions.h,如果要使用,记得先 import进来
//找地址
NSString *address = [db stringForQuery:@"SELECT Address FROM PersonList WHERE Name = ?",@"John”];
NSString *address = [db stringForQuery:@"SELECT Address FROM PersonList WHERE Name = ?",@"John”];
//找年齡
int age = [db intForQuery:@"SELECT Age FROM PersonList WHERE Name = ?",@"John”];
关闭数据库
- [FMDatabase close];
数据库的批量操作
使用FMDatabase 的executeStatements:或者executeStatements:withResultBlock:(是否需 要返回结果)
NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);"
"create table bulktest2 (id integer primary key autoincrement, y text);"
"create table bulktest3 (id integer primary key autoincrement, z text);"
"insert into bulktest1 (x) values ('XXX');"
"insert into bulktest2 (y) values ('YYY');"
"insert into bulktest3 (z) values ('ZZZ');";
success = [database executeStatements:sql];
或者
sql = @"select count(*) as count from bulktest1;"
}];
"select count(*) as count from bulktest2;"
"select count(*) as count from bulktest3;";
success = [self.db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) {
NSInteger count = [dictionary[@"count"] integerValue];
XCTAssertEqual(count, 1, @"expected one record for dictionary %@",
dictionary);
return 0;
}];
参数绑定
**INSERT INTO myTable VALUES (?, ?, ?)**
问号只是占位,执行操作可以使用NSArray, NSDictionary, or a va_list来匹配参数
你也可以选择使用命名参数语法:INSERT INTO myTable VALUES (:id, :name, :value)
参数名必须以冒名开头。SQLite本身支持其他字符($,@),Dictionary key的内部实现是冒号 开头。注意你的NSDictionary key不要包含冒号
NSDictionary *argsDict = @{@"name":@"Jason"};
[db executeUpdate:@"INSERT INTO myTable VALUES (:name)"withParameterDictionary:argsDict];
FMDatabaseQueue 及线程安全
不能使⽤用同⼀个FMDatabase在不同线程中操作,多线程的操作是通过FMDatabaseQueue实现
首先创建队列,然后把单任务包装到事务里,串行执行
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
FMResultSet *rs = [db executeQuery:@"select * from foo"];
while([rs next]) {
...
}
事务的回滚:(当前的队列的操作的取消)
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
if (whoopsSomethingWrongHappened) {
}
*rollback = YES;
return;
// etc...
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber
numberWithInt:4]];
}];
FMDatabaseQueue会在同一个队列里 同步
执行任务, GCD也会按它接收的块的顺序来执行
关于Setting Bundles
>
- Setting Bundle的概念更多地应该是在App的配置选择上
- Setting Bundle可以给用户提供一个从《设置》应用里去配置应用程序的方式
- 从开发者的角度来看,一般需要频繁修改的配置选项,如游戏的音量和控制选项等最好 放到app内部的设置页里,而类似于邮箱应用中的邮件地址和服务器的设置等不需要频 繁更改的配置项可以放到Setting Bundle里
- 从《设置》应用中进行设置,实际上是操作iOS配置系统中的应用程序域(Application Domain),是持久的
iOS的配置系统中存在如下一些域,将来查询时严格按照如下列出域的顺序进行查找
Domain | State |
---|---|
NSArgumentDomain | volatile(易失的) |
Application(Identified by the app’s identifier) | persistent(持久的) |
NSGlobalDomain | persistent |
Languages(Identified by the language names) | volatile |
NSRegisterationDomain | volatile |
registerDefaults:方法是在NSRegistrationDomain域上进行配置的,所以仅仅是存在于 内存中的,易失的