iOS应用数据存储的常用方式(沙盒Sandbox)

通常,我们很多的数据都不是临时或者一次性的,也就是说需要下一次登录以后,很多数据依然存在.我们就需要把这些需要的数据存储在一个文件夹或者文件中(或者网络).而ios默认就为我们提供了这样一个文件夹叫沙盒(就是文件系统目录).并且这个文件与其他文件系统隔离。应用必须待在自己的沙盒里,其他应用不能访问该沙盒(也就是说A程序的文件只能存在A的沙盒中,并且不能去访问B程序的沙盒)

常见的文件存储方式

  • XML属性列表(plist)归档
  • Preference(偏好设置)
  • NSKeyedArchiver归档(NSCoding)
  • SQLite3
  • Core Data
    要搞懂文件存储之前,我们应该对沙盒的结构以及沙盒的作用有深入的了解.

沙盒结构

  • Documents:保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录
  • tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录
  • Library/Caches:保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据
  • Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录

    所以我们应该合理的选择我们数据储存的地方.简单来说:

    1.完全不重要的数据放tmp.(临时文件)

    2.不重要数据,但是不任意删除数据存在Caches.(部分资源,缓存)

    3.重要数据,并且体积存在Document.(存档,歌曲..)

    4.重要数据,并且体积存在Preference.(用户设置,密码..)

沙盒获取方式:

NSString *home = NSHomeDirectory();

Document:

一般来说Document有2种方式来获取目录.

  • 根据沙盒的根目录拼接字符串

    NSString home = NSHomeDirectory();
    NSString
    documents = [home stringByAppendingPathComponent:@”Documents”];

    这个方法很垃圾.一般不建议使用.因为新版本的操作系统可能会修改目录名,所以会使老程序无法运行.并且字符串输入很容易打错.

  • 通过函数
    通过NSSearchPathForDirectoriesInDomains函数来取得目录结构. 详情介绍介意查看[NSSearchPathForDirectoriesInDomains深入解析]

    NSArray *array =  NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)

    因为NSSearchPathForDirectoriesInDomains的返回值是一个数组.并且在iOS中,只有一个目录跟传入的参数匹配,所以这个集合里面只有一个元素,所有又有另外一句常用语句.

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

     

Caches:

同Document一样.(字符串拼接的方式就不讲了)

NSString *Caches = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSCachesDirectory, YES) lastObject];
  ### tmp: tmp直接调用函数方法就好了.
tmp:NSString *tmp = NSTemporaryDirectory();
  ### **Preference**: Preference相对于上面3个文件(或文件夹)来说比较特殊,系统专门有一个类(NSUserDefaults). 第一步,我们需要创建一个NSUserDefaults的实例,然后通过get(响应set),set方法来使用.(具体使用后面会通过"偏好设置"讲到.) (补充了一点知识,查看文章> [[NSUserDefaults深入解析]](http://s-187595.abc188.com/index.php/2015/07/12/nsuserdefaults%E6%B7%B1%E5%85%A5%E8%A7%A3%E6%9E%90.html) ).   ## 文件存储 ### 属性列表(plist) 属性列表是一种非常常见并且简单的存储方式,XML格式的文件,拓展名为plist. Plist存储的对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型. 使用writeToFile:atomically:方法直接将对象写到属性列表文件中,使用xxxWithContentsOfFile:即可读取属性列表文件赋值给对象(其中xxx为对象类型,如dictionary,array等) 第一步,获取路径
    NSString *documents = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filepath = [docPath stringByAppendingPathComponent:@"me.plist"];
第二部,将对象写入文件
    NSArray *data = @[@"jack", @10, @"ffdsf"]; // 假设对象data
    [data writeToFile:filepath atomically:YES];
> 其中atomically参数是一个布尔类型.意思是如果为YES则保证文件的写入原子性,就是说会先创建一个临时文件,直到文件内容写入成功再导入到目标文件里. > > > 如果为NO,则直接写入目标文件里. 第三部,将文件读取并赋值给对象
    NSArray *data = [NSArray arrayWithContentsOfFile:filepath];
**若文件不能正常存储,可能是因为在NSArray或** **NSDictionary中存储了自定义对象.**   ### 偏好设置(Preference) 偏好设置一般是用来保存用户的用户名、密码、字体大小、是否自动登录等等设置.并且每个应用都有个NSUserDefaults实例(单例对象).并且本质是Plist. 具体详细说明请看 [ [NSUserDefaults深入解析]](http://s-187595.abc188.com/index.php/2015/07/12/nsuserdefaults%E6%B7%B1%E5%85%A5%E8%A7%A3%E6%9E%90.html)   第一步,拿到NSUserDefaults实例
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
第二部,通过NSUserDefaults的set方法,写入键值对
[defaults setObject:@"birdmichael" forKey:@"username"];
[defaults setFloat:18.0f forKey:@"text_size"];
[defaults setBool:YES forKey:@"auto_login"];
第三部,通过NSUserDefaults的get方法,读取键值对
NSString *username = [defaults stringForKey:@"username"];
float textSize = [defaults floatForKey:@"text_size"];
BOOL autoLogin = [defaults boolForKey:@"auto_login"];
**注意:**UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。 > 1.所以在99%的情况下,我们都需要在写入键值对后面调用synchornize方法.(可以理解为默认格式) > > > 2.一般来说,由于很容易把Key的字符串打错,我们经常把Key定义为宏.
[defaults synchornize];
#define BMUsername @"username"
#define BMtextSize @"text_size"
#define BMautoLogin @"auto_login"
  ### 归档(NSKeyedArchiver)(NSCoding) 归档是ios常见数据存储的一个重点也是一个小难点.归档(又名序列化),把对象转为字节码,以文件的形式存储到磁盘上.程序运行过程中或者当再次重写打开程序的时候,可以通过解归档(反序列化)还原这些对象。 **优点:1.大量数据,2.加密,3.支持自定义对象.** 我们将本小节内容分为3个部分来讲,1.对基本数据类型对象进行归档,2.键值对进行归档,3.数据模型进行归档. #### #### 基本数据类型归档(Foundation框架中对象) 很少用 第一步,获取文件路径
    NSString *documents = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filepath = [docPath stringByAppendingPathComponent:@"birdmichael.archive"];
> 因为归档后的文件是加密的,所以归档文件的扩展名可以随意取,甚至可以不要.但是一般来说扩展名都是.archive. 第二步,归档(序列化)
NSArray *archiveAry = @[@"bird",@"michael"];
[NSKeyedArchiver archiveRootObject: archiveAry toFile:filePath];
第三部,解码(反序列化)
NSArray *unArchiveAry = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
  • 基本数据类型归档总结:

  • 归档和解归档操作步骤简单

  • 一次只能归档一个对象,如果是多个对象归档需要分开进行
  • 归档的对象是Foundation框架中的对象
  • 归档和解归档其中任意对象都需要归档和解归档整个文件

####

键值对进行归档(自定义的内容) 几乎不用

第一步,获取文件路径

    NSString *documents = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filepath = [docPath stringByAppendingPathComponent:@"birdmichael.archive"];
第二步,NSData存放归档数据
NSMutableData *archiverData = [NSMutableData data];
第三步,取得归档对象
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiverData];
第四步,设置内容 (di'sa设置键值对)
 [archiver encodeObject:@"birdmichael" forKey:@"name"];
 [archiver encodeInt:20 forKey:@"age"];
 [archiver encodeObject:@[@"ios",@"oc"] forKey:@"language"];
第五部,结束编辑,并写入文件.
[archiver finishEncoding];
[archiverData writeToFile:filePath atomically:YES]
解档: 第一步,获取文件路径
    NSString *documents = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filepath = [docPath stringByAppendingPathComponent:@"birdmichael.archive"];
第二步,读取文件,生成实例
NSData *unarchiverData = [NSData dataWithContentsOfFile:filePath];
第三步,取得解档对象
NSKeyedUnarchiver *unachiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:unarchiverData];
第四步,根据key取值.
NSString *name = [unachiver decodeObjectForKey:@"name"];
int age = [unachiver decodeIntForKey:@"age"];
NSArray *ary = [unachiver decodeObjectForKey:@"language"];
  • 键值对进行归档总结:

  • 在带键的归档中,每个归档字段都有一个key值,解归档时key值要与归档时key值匹配

  • 带键归档可以一次存储多个对象
  • 归档的对象是Foundation框架中的对象
  • 归档和解归档其中任意对象都需要归档和解归档整个文件
     

 

数据模型进行归档(自定义对象) 常用率高达95%

第一步,新建数据模型(对象).并申明属性

#import <Foundation/Foundation.h>
@interface BMStudent : NSObject
@property (nonatomic, copy) NSString *no;
@property (nonatomic, assign) double height;
@property (nonatomic, assign) int age;
@end
第二步,遵守协议NSCoding
#import <Foundation/Foundation.h>
@interface BMStudent : NSObject  <NSCoding>
@property (nonatomic, copy) NSString *no;
@property (nonatomic, assign) double height;
@property (nonatomic, assign) int age;
@end
第三步,写入需要存储或读取的属性并写入对应的Key,
#import "BMStudent.h"
@interface BMStudent() 
@end
@implementation BMStudent
/**
 *  将某个对象写入文件时会调用
 *  在这个方法中说清楚哪些属性需要存储
 */
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.no forKey:@"no"];
    [aCoder encodeInt:self.age forKey:@"age"];
    [aCoder encodeDouble:self.height forKey:@"height"];
}
/**
 *  从文件中解析对象时会调用
 *  在这个方法中说清楚哪些属性需要存储
 */
- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        // 读取文件的内容
        self.no = [aDecoder decodeObjectForKey:@"no"];
        self.age = [aDecoder decodeIntForKey:@"age"];
        self.height = [aDecoder decodeDoubleForKey:@"height"];
    }
    return self;
}
@end
第四步,获取文件路径
    NSString *documents = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filepath = [docPath stringByAppendingPathComponent:@"birdmichael.archive"];
第五部,归档写入文件(一般丢入对象数组既数据模型)
    [NSKeyedArchiver archiveRootObject:stu toFile:filepath];

// 假设之前创建了stu对象,并写入了值
    BMStudent *stu = [[MJStudent alloc] init];
    stu.no = @"42343254";
    stu.age = 20;
    stu.height = 1.55;
解档 第一步,获取文件路径
    NSString *documents = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filepath = [docPath stringByAppendingPathComponent:@"birdmichael.archive"];
第二步,从文件读取对象(一般读书到对象数组既数据模型)
BMStudent *stu = [NSKeyedUnarchiver unarchiveObjectWithFile:filepath];

// 取得数据用.语法就好了.
    NSLog(@"%@ %d %f", stu.no, stu.age, stu.height);
  • 数据模型进行归档总结:

  • 自定义对象与自定义内容归档和解归档步骤和用法完全相同

  • 自定义的对象归档需要实现NSCoding协议,并且实现协议中的方法
  • NSCoding协议中有两个方法:
  • 1.encodeWithCoder方法对对象属性进行编码,在对象归档时调用
  • 2.initWithCoder方法解码归档数据来初始化对象,在对象解归档时调用

数据库方式(SQLite3)(Core Data)

涉及内容太多,后期单独详解