这篇文章将会介绍数据持久化的4种方式:NSUserDefaults,NSFileManager,NSCoding+NSKeyedArchiver和CoreData
1.NSUserDefaults
Cocoa会为每个app自动创建一个文件,我们可以用它来存储一些较简单的数据,如音效等应用设置之类的少量信息。NSUserDefaults本质上是以Key-Value形式存成plist文件,放在/Library/Preferences/{Bundle Identifier}.plist目录下,这个文件是不安全的,所以不要用NSUserDefaults来存储密码之类的敏感信息。
NSUserDefaults是一个单例,它支持保存的数据类型有:NSNumber,NSString, NSDate, NSArray, NSDictionary, BOOL.
1.写入数据
// 获取一个NSUserDefaults对象
NSUserDefaults * aUserDefaults = [NSUserDefaults standardUserDefaults];
// 插入一个key-value值
[aUserDefaults setObject:_label.text forKey:@"name"];
// 同步操作,为了把设置及时写入文件,防止由于崩溃等情况App内存信息丢失
[aUserDefaults synchronize];
2.读取数据
NSUserDefaults * aUserDefaults = [NSUserDefaults standardUserDefaults];
// 获取一个key-value值
NSString * aStr = [aUserDefaults objectForKey:@"name"];
值得注意的是:NSUserDefaults存储的对象全是不可变的,例如,如果我想要存储一个 NSMutableArray对象,我必须先创建一个不可变数组:
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"123",@"234", nil];
NSArray * array = [NSArray arrayWithArray:mutableArray];
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
[user setObject:array forKey:@"name"];
取出数据也一样:
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:[user objectForKey:@"name"]];
2. NSFileManager
上面提到NSUserDefaults可以存成Plist文件,只是Apple帮我们封装好了读写方法而已(事实上我们自己实现这个方法也很简单)。NSUserDefaults的缺陷是存储只能是Library/Preferences/<Application BundleIdentifier>.plist 这个文件,如果我们要自己写一个Plist文件呢? 使用NSFileManger可以很容易办到。
如果你存储的数据是Plist文件支持的类型,直接用NSFileManager的writToFile接口就可以写入一个plist文件了。Plist文件支持的数据格式有:NSString,NSNumber, Boolean, NSDate,NSData,NSArray和NSDictionary.其中,Boolean格式事实上以[NSNumber numberOfBool:YES/NO];这样的形式表示。
1).获取路径:
NSFileManager *fileManager = [NSFileManager defaultManager];
//Library目录路径:NSLibraryDirectory, Cache目录路径:NSCachesDirectory
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
//等同于以下操作
//NSString * documentsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
NSString *directoryPath = [documentsPath stringByAppendingPathComponent:@"file"];
2).在上面的路径下创建目录:createDirectoryAtPath
if (![fileManager fileExistsAtPath:directoryPath]) {
[fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:NO attributes:nil error:nil];
}]
3).在上面的目录下创建文件:createFileAtPath
[fileManager createFileAtPath:[documentsPath stringByAppendingString:@"/text.plist"] contents:data attributes:nil];
//获取创建的该文件的路径
filePath = [NSString stringWithFormat:@"%@%@",documentsPath,@"test.plist"];
4).写数据到文件:writeToFile
NSDictionary *dictionary = @{@"key":@"value"};
dictionary writeToFile:filePath atomically:YES];
5).读取文件的属性:attributesOfItemAtPath
例如,读取文件的创建日期:
NSDate *creationDate = nil;
if ([fileManager fileExistsAtPath:filePath]) {
NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
creationDate = attributes[NSFileCreationDate];
}
6).删除文件:removeItemAtPath
NSError *error = nil;
if (![fileManager removeItemAtPath:filePath error:&error]) {
NSLog(@"[Error] %@ (%@)", error, filePath);
}
3.NSKeyedArchiver/NSKeyedUnarchiver
上面介绍的两种方法,通常仅支持常用数据类型,但是不支持自定义的数据类型,不过Cocoa提供了NSCoding和NSKeyArchiver两个工具类,可以把我们自定义的对象编码成二进制数据流,然后存进文件里面,如果要使用这种方式进行存储,首先自定义的对象要继承NSCoding的delegate。
NSCoding是一个简单的协议,有两个方法:-initWithCoder: 和 encodeWithCoder:。遵循NSCoding协议的类可以被序列化和反序列化,这样可以归档到磁盘上或分发到网络上。
@interface Book : NSObject <NSCoding>
@property NSString *author;
@property NSUInteger price;
@end
@implementation Book
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) {
return nil;
}
self.author = [decoder decodeObjectForKey:@"author"];
self.price = [decoder decodeIntegerForKey:@"price"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.author forKey:@"author"];
[encoder encodeInteger:self.price forKey:@"price"];
}
@end
如上,NSCoding 主要是样板文件。每个属性用属性名作为key,编码或解码成一个对象或者类型。这样,我们就定义了一个可以使用NSCoding进行编码的数据对象。
(Mantle是一个意在减少写NSCoding样板文件的类库,可以看看)
Archiving(归档):
Book *books = [Book alloc] init];
//归档至目录
[NSKeyedArchiver archiveRootObject:books toFile:@"/path/to/archive"];
//归档至二进制数据
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:books];
Unarchiving(解档):
//从目录解档
[NSKeyedUnarchiver unarchiveObjectWithFile:@"/path/to/archive"];
//从文件解档
Book *book = [NSKeyedUnarchiver unarchiveObjectWithData:data];
除了归档到文件,还可以归档到NSUserDefaults。
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:books];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"books"];
Unarchiving:
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"books"];
NSArray *books = [NSKeyedUnarchiver unarchiveObjectWithData:data];
所以其实使用NSCoding和NSKeyedArchiver事实上也是写入和读取文件,只不过对复杂对象进行了编码使得文件支持更多数据类型而已。
下一节我们介绍CoreData