iOS数据持久化——属性列表和归档

iOS数据持久化的方式分为三种

属性列表 (自定义的Property List 、NSUserDefaults)

归档 (NSKeyedArchiver)

数据库 (SQLite、Core Data、第三方类库等)

本文只介绍:属性列表和归档

一. 属性列表

Plist一般用于存储Dictionary、Array、Boolean、Data、Date、Number、String这些类型的数据,但Boolean、Data、Date、Number、String类型的数据一般不直接存入Plist文件中,因为Plist文件有分层的概念,一般用NSDictionary或NSArray作为容器,再把其他数据类型装入,最后把容器存入Plist文件。Plist文件不能直接存储自定义的类,需要进行转化成上述的数据类型,才能存储。

(一). 数据的简单存储

NSDictionary或NSArray使用writeToFile:atomically:方法,会默认存储到Plist文件

    // 获取沙盒中documents目录的路径
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

    // 拼接路径
    NSString *someObjPath = [documentsPath stringByAppendingPathComponent:@"someObj.plist"];

    // 要写入文件的数组
    NSArray *someObj = @[@"AAA",@"BBB",@999,@'A',@[@{@"name":@"小明"}]];

    // 把数组写入plist文件
    // 第一个参数是文件名,第二个参数为是否使用辅助文件,如果为YES,则先写入到一个辅助文件,然后辅助文件再重新命名为目标文件,
    // 如果为NO,则直接写入到目标文件
    [someObj writeToFile:someObjPath atomically:YES];

(二). NSFileManager常用的文件管理

  1. 创建目录 createDirectoryAtPath:

  2. 创建文件 createFileAtPath:

  3. 删除某个文件 removeItemAtPath:

  4. 检查某个文件是否存在 fileExistsAtPath:

  5. 检查文件是否可读 isReadableFileAtPath:

  6. 是否可写:isWritableFileAtPath:

  7. 取得文件属性 fileAttributesAtPath:

  8. 改变文件属性changeAttributesAtPath:

  9. 从path代表的文件中读取数据:contentsAtPath

  10. 移动文件movePath:toPath:handler:

  11. 复制文件copyPath:toPath:handler:

    // 1. 在沙盒里新建目录,再在目录里新建文件夹
    // 创建一个文件管理器
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // 获取沙盒下的documents路径
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;

    // 拼接路径
    NSString *newFilePath = [documentsPath stringByAppendingPathComponent:@"NewFile"];

    // 创建新目录
    [fileManager createDirectoryAtPath:newFilePath withIntermediateDirectories:YES attributes:nil error:nil];


      // 读写数据一
    // 2. 向fileNew.plist写入数据
    // 把路径改变到新路径下
    [fileManager changeCurrentDirectoryPath:[newFilePath stringByExpandingTildeInPath]];

    // 创建fileNew.plist文件
    [fileManager createFileAtPath:@"fileNew.plist" contents:nil attributes:nil];

    // 写入数据
    NSArray *dataArr = @[@[@100,@"study"],@{@"name":@"xiaoming"}];

    // 拼接fileNew.plist路径
    NSString *dataPath = [newFilePath stringByAppendingPathComponent:@"fileNew.plist"];

    // 把数组写入fileNew.plist
    [dataArr writeToFile:dataPath atomically:YES];


    // 3. 读取fileNew.plist数据
    NSArray *arrData = [NSArray arrayWithContentsOfFile:dataPath];

    NSLog(@"%@",arrData);



    // 读写数据二
    // 使用NSMutableData存储数据要记住存储的顺序,读取数据时要要根据那个顺序计算每个数据所处的位置
    // 2. 向fileName文件写入数据
    // 待写入的数据
    NSString *dataStr = @"New Friend";
    int dataInt = 666;
    float dataFloat = 3.14159f;

    // 拼接存储数据的路径
    NSString *dataPath2 = [newFilePath stringByAppendingPathComponent:@"fileName"];

    //创建数据缓冲
    NSMutableData *writerData = [[NSMutableData alloc] init];

    //将字符串添加到缓冲中
    [writerData appendData:[dataStr dataUsingEncoding:NSUTF8StringEncoding]];

    //将其他数据添加到缓冲中
    [writerData appendBytes:&dataInt length:sizeof(dataInt)];
    [writerData appendBytes:&dataFloat length:sizeof(dataFloat)];

    //将缓冲的数据写入到文件中
    [writerData writeToFile:dataPath2 atomically:YES];


    // 3. 读取数据:
    // 读取fileName文件中的数据
    NSData *readerData = [NSData dataWithContentsOfFile:dataPath2];

    // 计算dataStr在dataStr的区间,并获取字
    dataStr = [[NSString alloc] initWithData:[readerData subdataWithRange:NSMakeRange(0, [dataStr length])]
                                   encoding:NSUTF8StringEncoding];

    // 计算dataInt在dataStr的区间,并获取
    [readerData getBytes:&dataInt range:NSMakeRange([dataStr length], sizeof(dataInt))];

    // 计算dataFloat在dataStr的区间,并获取
    [readerData getBytes:&dataFloat range:NSMakeRange([dataStr length] + sizeof(dataInt), sizeof(dataFloat))];

    NSLog(@"dataStr:%@   dataInt:%d   dataFloat:%f", dataStr,dataInt,dataFloat);


    // 删除fileName文件
    [fileManager removeItemAtPath:@"fileName" error:nil];

    // 删除一个目录:
    // currentDirectoryPath:当前目录
    NSLog(@"%@",fileManager.currentDirectoryPath);
    [fileManager removeItemAtPath:fileManager.currentDirectoryPath error:nil];

二. 偏好设置

(一). 知识点

  1. 偏好设置的本质还是plist文件存储,相对于Plist文件存储来讲存储数据更快捷,不需要获取全路径。

  2. 偏好设置用来保存应用程序设置和属性、用户保存的数据,当用户再次打开程序或开机后这些数据仍然存在。

  3. 很多iOS应用都支持偏好设置,比如保存用户名、密码、字体大小等设置,iOS也提供了一套标准的解决方案来为应用加入偏好设置功能。
  4. 每个iOS应用都有一个NSUserDefaults实例,它是一个单例对象,通过它来存取偏好设置,设置信息都是键值对的形式。
  5. 用UserDefaults实例设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘,所以调用了set方法之后数据有可能还没有写入磁盘,如果应用程序突然终止了,数据有可能写入不成功。
  6. 偏好设置是专门用来保存应用程序的配置信息的, 一般情况不要在偏好设置中保存其他数据。
  7. 如果利用系统的偏好设置来存储数据, 默认就是存储在Preferences文件夹下面的,偏好设置会将所有的数据都保存到同一个文件中。
  8. 偏好设置针对同一个关键字对应的对象或者数据,可以对它进行重写,重写之后关键字就对应新的对象或者数据,旧的对象或者数据会被自动清理。
  9. 使用偏好设置对数据进行保存之后, 它保存到系统的时间是不确定的,会在将来某一时间点自动将数据保存到Preferences文件夹下面,如果需要即刻将数据存储,可以使用[defaults synchronize];强制写入偏好设置文件。
  10. iOS7之后,如果不写[defaults synchronize];,数据也会同步存储。

(二). 具体使用

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

    [userDefaults setValue:@"小明" forKey:@"name"];
    [userDefaults setValue:@"男" forKey:@"sex"];
    [userDefaults setValue:@"学生" forKey:@"work"];

    // 写入磁盘
    [userDefaults synchronize];

    NSLog(@"%@",[userDefaults objectForKey:@"name"]);
    NSLog(@"%@",[userDefaults objectForKey:@"sex"]);
    NSLog(@"%@",[userDefaults objectForKey:@"work"]);

三. 归档与解档

(一). 知识点

  1. 归档又名序列化,把对象转为字节码,以文件的形式存储到磁盘上;程序运行过程中或者重新打开程序时,可以通过解归档(反序列化)还原这些对象。
  2. 不是所有的对象都可以直接用这种方法进行归档,只有遵守了NSCoding协议的对象才可以,如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,可以直接用NSKeyedArchiver进行归档。
  3. 每次归档对象时,都会调用encodeWithCoder:方法;一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:方法归档实例变量。
  4. 每次从文件中恢复(解码)对象时,都会调用initWithCoder:方法;一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey方法解码实例变量。
  5. 归档和解归档可以用于少量数据的持久化存储和读取,且通过归档创建的文件是加密的

缺点:在归档的形式来保存数据,只能一次性归档保存以及一次性解压,所以只能针对小量数据。而且对数据操作比较笨拙,即如果想改动数据的某一小部分,还是需要解压整个数据或者归档整个数据。

(二). 具体使用

归档方式有以下三种:
a. 对Foundation框架中对象进行归档
b. 对自定义的内容进行归档
c. 对自定义的对象进行归档

1. 对Foundation框架中对象进行归档

    // 获取文件路径
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    // 拼接路径
    NSString *dataPath = [documentsPath stringByAppendingPathComponent:@"NewData.data"];

    // 待归档的数据
    NSArray *dataArr = @[@"天天",@"编程"];

    // 归档
    if ([NSKeyedArchiver archiveRootObject:dataArr toFile:dataPath]) {
        NSLog(@"Archiver success !");
    }

    // 解档
    NSArray *data = [NSKeyedUnarchiver unarchiveObjectWithFile:dataPath];

    NSLog(@"%@%@",data[0],data[1]);

2. 对自定义的内容进行归档

    // 获取文件路径
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    // 拼接路径
    NSString *dataPath = [documentsPath stringByAppendingPathComponent:@"NewData.data"];

    // 创建存储数据的NSData对象
    NSMutableData *dataM = [NSMutableData data];

    // 创建归档对象
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:dataM];

    // 添加归档内容
    [archiver encodeObject:@"小明" forKey:@"name"];
    [archiver encodeObject:@"男" forKey:@"sex"];
    [archiver encodeInteger:19 forKey:@"age"];

    // 完成归档内容
    [archiver finishEncoding];

    // 写入磁盘
    if ([dataM writeToFile:dataPath atomically:YES]) {
        NSLog(@"Archiver success !");
    }


    // 解档
    // 获取数据
    NSData *data = [NSData dataWithContentsOfFile:dataPath];

    // 创建解档对象
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    // 解档
    NSString *name = [unarchiver decodeObjectForKey:@"name"];
    NSString *sex = [unarchiver decodeObjectForKey:@"sex"];
    NSInteger age = [unarchiver decodeIntForKey:@"age"];


    NSLog(@"%@  %@  %ld",name,sex,age);

3. 对自定义的对象进行归档

---------- Person.h 文件

#import 

@interface Person : NSObject <NSCoding>

@property (nonatomic,strong) NSString *name;

@property (nonatomic,strong) NSString *sex;

@property (nonatomic,assign) NSInteger age;

@end

---------- Person.m 文件

#import "Person.h"

@implementation Person 

// 归档
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeObject:self.sex forKey:@"sex"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}

// 解档
- (instancetype)initWithCoder:(NSCoder *)aDecoder {

    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.sex = [aDecoder decodeObjectForKey:@"sex"];
        self.age = [aDecoder decodeIntegerForKey:@"age"];

    }
    return self;
}

@end

---------- 对Penson对象进行归档与解档

    // 获取Documents的路径
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    // 拼接路径
    NSString *path = [documentPath stringByAppendingPathComponent:@"person.data"];

    //
    Person *per = [[Person alloc] init];
    per.name = @"xiaoming";
    per.sex = @"男";
    per.age = 20;

    // 归档
    [NSKeyedArchiver archiveRootObject:per toFile:path];

    // 解档
    Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

    NSLog(@"%@",person.sex);

你可能感兴趣的:(iOS-UI界面设计,数据持久化,数据存储,属性列表,归档,plist)