数据存储的三种方式
读取info.plist文件:
NSDictionary *infoDict = [NSBundle mainBundle].infoDictionary;
NSString *version = infoDict[@"CFBundleVersion"];
一,属性列表
属性列表即plist文件,实质是xml文件。
注意点:
- 根节点只能是NSArray或者NSDictionary
- 如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中
-
将一个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];
二,偏好设置
用于保存APP的设置,例如是否记住密码、是否接受推送、保存字体大小等。
// 保存数据
-(void)saveData
{
// 1.获取偏好设置
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
// 2.保存数据
[userDefaults setObject:@"传智播客" forKey:@"itcastKey"];
[userDefaults setBool:YES forKey:@"isGoodKey"];
/*注意:UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法强制写入
*/
// 3.同步数据(iOS8)
[userDefaults synchronize];
}
// 读取数据
-(void)readData
{
// 1.获取偏好设置
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
// 2.读取数据
NSLog(@"%@",[userDefaults objectForKey:@"itcastKey"]);
}
三,归档可以将任意类型的对象,编码后以二进制的形式保存。
在使用plist进行数据存储和读取,只适用于系统自带的一些常用类型才能用,且必须先获取路径相对麻烦;
偏好设置(将所有的东西都保存在同一个文件夹下面,且主要用于存储应用的设置信息)
归档优势:因为前两者都有一个致命的缺陷,只能存储常用的类型。归档可以实现把自定义的对象存放在文件中。
注意点:
- 需要归档的自定义类,必须实现
<NSCoding>
协议,告诉系统需要归档和解档哪些属性。 - 若对象的属性中有自定义类型,则该类也需要实现
<NSCoding>
协议。
// 保存数据
-(void)saveData
{
// 1.创建对象
CZPerson *person = [CZPerson new];
person.name = @"Jack";
person.age = 18;
// 2.实现<NSCoding>协议
// 3.获取文件路径
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [documents stringByAppendingPathComponent:@"person.data"];
// 4.编码并保存(归档)
[NSKeyedArchiver archiveRootObject:person toFile:filePath];
}
// 读取数据
-(void)readData
{
// 1.获取文件路径
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [documents stringByAppendingPathComponent:@"person.data"];
// 2.解档
CZPerson *person = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"%@",person.name);
}
实现
<NSCoding>
协议 • encodeWithCoder什么时候调用:对象归档时候调用
• encodeWithCoder作用:告诉系统对象里的哪些属性需要归档,怎么去归档,根据一个key去归档,目的就是以后取的时候,也根据这个key去取数据。
// 编码
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeInteger:_age forKey:@"age"];
}
• initWithCoder作用:告诉系统对象里的哪些属性需要解档,怎么去解档,根据之前存储的key去解档
• initWithCoder是一个初始化方法,需要先初始化父类的,但是不能调用[super initWithCoder:],因为父类NSObject没有遵守NSCoding协议。
3> initWithCoder什么时候需要调用[super initWithCoder:]
• initWithCoder原理:只要解析文件就会调用,xib,storyboard都是文件,因此只要解析这两个文件,就会调用initWithCoder。
• initWithCoder是一个初始化方法,需要先初始化父类的,但是不能调用[super initWithCoder:],因为父类NSObject没有遵守NSCoding协议。
3> initWithCoder什么时候需要调用[super initWithCoder:]
• initWithCoder原理:只要解析文件就会调用,xib,storyboard都是文件,因此只要解析这两个文件,就会调用initWithCoder。
• 因此如果在storyboard使用自定义view,重写initWithCoder方法,一定要调用[super initWithCoder:],因为只有系统才知道怎么解析storyboard,如果没有调用,就解析不了这个文件。
// 解码,实例化
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init])
{//将对象的一些属性保存,没有设置的属性不作保存
_name = [aDecoder decodeObjectForKey:@"name"];
_age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
两个方法的区别与使用场景
/// 解归档 : 这个方法是把视图从bundle加载到内存.视图的frame可能不准
//- (instancetype)initWithCoder:(NSCoder *)aDecoder
//{
// if (self = [super initWithCoder:aDecoder]) {
// NSLog(@"%s",__func__);
// }
// return self;
//}
/// 视图被唤醒的方法.在这个方法里面可以拿到准确frame的视图
- (void)awakeFromNib
//- (instancetype)initWithCoder:(NSCoder *)aDecoder
//{
// if (self = [super initWithCoder:aDecoder]) {
// NSLog(@"%s",__func__);
// }
// return self;
//}
/// 视图被唤醒的方法.在这个方法里面可以拿到准确frame的视图
- (void)awakeFromNib
{
}
//读取JSON文件,转模型
+ (NSArray *)projects {
// 1. 文件路径
NSString *path = [[NSBundle mainBundle] pathForResource:@"more_project.json" ofType:nil];
// 2. 读取文件内容 - 二进制数据
NSData *data = [NSData dataWithContentsOfFile:path];
// 3. 解析JSON 专门用来解析JSON的类 NSJSONSerialization
// 1. 文件路径
NSString *path = [[NSBundle mainBundle] pathForResource:@"more_project.json" ofType:nil];
// 2. 读取文件内容 - 二进制数据
NSData *data = [NSData dataWithContentsOfFile:path];
// 3. 解析JSON 专门用来解析JSON的类 NSJSONSerialization
/**
看到options先传0.没有达到效果再来看枚举值
*/
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// 声明一个可变的数组
NSMutableArray *projects = [NSMutableArray array];
// 4. 遍历数据,把字典转成模型 - 使用block来遍历数组
[json enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
CZProject *project = [CZProject projectWithDict:obj];
[projects addObject:project];
*/
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// 声明一个可变的数组
NSMutableArray *projects = [NSMutableArray array];
// 4. 遍历数据,把字典转成模型 - 使用block来遍历数组
[json enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
CZProject *project = [CZProject projectWithDict:obj];
[projects addObject:project];
}];
// 将可变数组,变成不可变 : 将线程不安全的类,变成了线程安全的类.同时,不可变的数组,外界不能修改的.
return projects.copy;
}