数据持久化

这篇文章将会介绍数据持久化的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/.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 
@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事实上也是写入和读取文件,只不过对复杂对象进行了编码使得文件支持更多数据类型而已。

参考:Apple文档,nshipster,iOS数据持久化

下一节我们介绍CoreData

你可能感兴趣的:(数据持久化)