前段日子在找工作的过程中,面试过程中基本会被问及数据持久化的问题。虽然数据持久化在平时的开发过程中经常使用到,但是对其的认识还是停留在使用的阶段,对文件夹和文件一些信息还是含糊不清。接下来就梳理一下 iOS 中的数据持久层吧。
本文主要从下面三个方面去梳理:
1.数据持久层有哪些方式?
2.各数据持久层实现方式所保存的文件路径
3.不同方式的简单实现
数据持久层4种方式
1.preference:偏好设置,是以项目bundle identifier为命名的plist文件,使用 iOS 提供的 UserDefault 单例使用。该方式是存储少量数据的最佳实现方式。比如保存用户所选择的设置信息,不合适保存大量消息。其能够保存的数据对象有:NSData, NSString, NSNumber, NSData, NSArray and NSDictionary,但是简单数据类型如 bools and integers 也是可以保存的。其缺点就是不能保存自定义类对象。(当然也可以通过将自定义对象转换成 NSData 来实现,可见下文🌰)
2.plist文件:其实是XML属性列表,后缀名为plist. preference也是plist文件。
3.归档:本质是Data数据的arc格式,用于存储自定义类对象。NSKeyedArchiver和NSKeyedUnarchiver是NSCoder的具体子类,它们只是意味着它们是可用于对磁盘(也称为归档)上的数据进行编码/解码的类。
4.SQLite:iOS 相关api都是C语言接口,且设计的易用性不强,数据存储在.db3数据库中,可以存储大量数据,存储、检索大量数据非常高效。鉴于直接使用C语言进行的操作不便,便有了FMDB(考虑并发安全性,如何更好的处理事务等的sqlite的面向对象的封装)。
5.CoreData:苹果自身的数据存储框架,可以直接将对象存储于数据库中,类似Java中的hibernate。
疑问:UserDefault 与 plist文件到底有什么区别呢?
1.在形式上,两者都是属于plist文件,并无任何区别。
2.效率:对于两者存储少量数据,哪个效率更高?两者之间也没有实质上的区别,如果硬要说要区别的话,那大概是 UserDefault 是官方实现的一个单利,实现和获取都是很方便。
3.所处的文件夹:这是两者最大的区别,UserDefault 是官方定义的,存储于 ../Library/Preferences/bundle identifier.plst;而plist 文件任由你存储于沙盒中的任意位置。假设存储于 Document 文件夹,该文件夹中的文件将会被同步到 iCloud 中,当用户备份设备的时候,文件也会被备份,这样就会减少用户所被分配的 iCloud 存储空间,对于是否将文件存储于 Document 中,取决于你是否需要保留这些数据。
文件夹一览图
这里说明一下 Caches 和 tmp 的区别:两者都可以用于缓存比较大的文件,如音频等;主要区别是前者在程序再次启动后,之前保存于其中的文件还存在,而保存在 tmp 中就会被清空了。
各方式的简单实现
1.preference
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"123",@"234",@"999", nil];
NSArray *array = [NSArray arrayWithArray:mutableArray];
[[NSUserDefaults standardUserDefaults]setObject:array forKey:@"记住存放的数据一定是不可变的"];
[[NSUserDefaults standardUserDefaults] synchronize];//数据同步
NSMutableArray *mutableA = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults]objectForKey:@"记住存放的数据一定是不可变的"]];
NSLog(@"可变数组 = %@",mutableA);
2.plist文件
NSString *pathDic = @"/Users/qiushaoyi/Desktop/数据存储 大全/plist文件夹/年龄字典.plist";
NSDictionary *ageDic = @{@"qiushaoyi":@"18",@"wuna":@"16",@"ffffff":@"12",};
[ageDic writeToFile:path1 atomically:YES];
// 读取文件
NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:path1];
[dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
NSLog(@"dic[%@] = %@",key,obj);
}];
3.归档
@interface Person : NSObject<NSCoding>
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@property (nonatomic,assign) float height;
@property (nonatomic,assign) NSDate *birthday;
@end
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (NSUInteger i = 0; i < count; i ++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
free(ivars);
}
===========================================================
@implementation Person
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (NSUInteger i = 0; i < count; i ++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivars);
}
return self;
}
===========================================================
Person *per1 = [[Person alloc] init];
per1.name = @"笑话";
per1.age = 17;
per1.height = 179.f;
// nsdate和dateFormatter绑定使用的
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"yyyy-MM-dd";
per1.birthday = [dateFormatter dateFromString:@"1990-10-10"];
Person *per2 = [[Person alloc] init];
per2.name = @"woqu";
per2.age = 18;
per2.height = 109.f;
per2.birthday = [dateFormatter dateFromString:@"1998-03-9"];
NSMutableArray *peraArray = [NSMutableArray arrayWithObjects:per1,per2, nil];
NSString *personStr = [pathStr stringByAppendingPathComponent:@"personStr.arc"];
[NSKeyedArchiver archiveRootObject:peraArray toFile:personStr];
NSMutableArray *personGetArray = [NSKeyedUnarchiver unarchiveObjectWithFile:personStr];
[personGetArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"4:person[%lu]: %@",idx,obj);
}];
4.SQLite
-(void)createTable{
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.db"];
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
_db = [FMDatabase databaseWithPath:path];
if ([_db open]) {
NSString *sql = @"CREATE TABLE User (Id integer PRIMARY KEY AUTOINCREMENT,name text,screenName text, profileImageUrl text,mbtype text,city text);"
"CREATE TABLE Status (Id integer PRIMARY KEY AUTOINCREMENT,source text,createdAt date,\"text\" text,user integer REFERENCES User (Id))";
[_db executeStatements:sql];
}
}
}
对表增删改查:
-(void)addStatus:(KCStatus *)status{
NSString *sql=[NSString stringWithFormat:@"INSERT INTO Status (source,createdAt,\"text\" ,user) VALUES('%@','%@','%@','%@')",status.source,status.createdAt,status.text,status.user.Id];
BOOL isSuccessful = [_db executeUpdate:sql];
if (isSuccessful) {
NSLog(@"add status successfully!!");
}else{
NSLog(@"error = %@", [_db lastErrorMessage]);
}
}
-(void)removeUserByName:(NSString *)name{
NSString *sql=[NSString stringWithFormat:@"DELETE FROM User WHERE name='%@'",name];
BOOL isSuccessful = [_db executeUpdate:sql];
if (isSuccessful) {
NSLog(@"delete user successfully!!");
}else{
NSLog(@"error = %@", [_db lastErrorMessage]);
}
}
-(NSArray *)getAllStatus{
NSString *sql=@"SELECT Id, source,createdAt,\"text\" ,user FROM Status ORDER BY Id";
//执行查询SQL语句,返回查询结果
FMResultSet *result = [_db executeQuery:sql];
NSMutableArray *array = [NSMutableArray array];
//获取查询结果的下一个记录
while ([result next]) {
//根据字段名,获取记录的值,存储到字典中
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
int num = [result intForColumn:@"Id"];
NSString *name = [result stringForColumn:@"source"];
NSString *sex = [result stringForColumn:@"createdAt"];
dict[@"Id"] = @(num);
dict[@"source"] = name;
dict[@"createdAt"] = sex;
//把字典添加进数组中
[array addObject:dict];
}
return array;
}
5.CoreData
该部分可以参考ObjC 中国
本文是我自己对数据持久层的理解和梳理,如果有什么不对的地方请私信我,谢谢!
本文所对应的demo
参考:BestWeek