概述
HMCache 是适用于 OS X 和 iOS 应用程序数据持久化的库,包含一些其他有用功能。它是从头开始编写的,并由苹果的 NSCoding/NSCoder 驱动。HMCache 实现了一套对象,这些对象协同工作以提供非常方便的持久化 API。这些对象由以下几种:
- HMObject 是一个基类,其子类可以自动扫描自身及其超类(至 NSObject)的属性名称,并将每个版本的类键保存到磁盘上。这些版本的记录将用于通过 NSCoding 方法
initWithCoder:
和encodeWithCoder:
序列化和反序列化 HMObject 子类的实例,并从早期序列化数据迁移到当前版本类结构并初始化一个实例。 - HMCacheManager 是一个文件缓存引擎,提供键值风格 API。它是单例。最低 API 用于使用一个键名写入一个 NSData 到文件,并使用相同的键从磁盘读取一个 NSData。它还提供了 'group' 的概念,实际上是一个目录。使用具有键和组的读写 API 可以在特定组中排序键。
- HMMigrationData 用于从 HMObject 子类实例迁移序列化缓存的数据。在反序列化缓存数据并检测到数据是来自旧版本时,将会新建一个 HMMigrationData 对象,并将所有 HMObject 子类对象的缓存值读取并设置为 HMMigration 数据实例。然后,HMMigration 数据实例将通过
- (BOOL)migrateWithData:(HMMigrationData *)migrationData fromVersion:(NSString *)version
方法传递给 HMObject 子类。子类必须根据旧版本和当前版本之间的差异,修改、删除用于迁移目的的关键值。
额外内置功能
- 自动实现子类的 NSCopying 协议。
- 实现
isEqual:
- 实现
- (NSUInteger)hash
- 自动实现一个 JSON 格式的
- (NSString *)description
和- (NSString *)debugDescription
- 上述特性是否支持 分类 属性。
开始使用
下载或查看HMCache的最新版本,然后将整个“HMCache”子文件夹添加到您的Xcode项目中。
或者,您可以使用CocoaPods安装HMCache,只需在您的Podfile中添加以下行
pod "HMCache", "~> 0.1.5"
最后运行 $ pod install
。
Hello World
HMObject子类
从HMObject派生与从其他Cocoa类如NSObject派生无异。
#import "HMObject"
@interface FooObject : HMObject
@property (nonatomic, copy) NSString *string;
@property (nonatomic, strong) NSNumber *number;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) NSArray *array;
@property (nonatomic, strong) BarObject *barObject;
@end
缓存和恢复
NSString *key = @"key";
FooObject *foo = [FooObject objectInCacheForKey:key];
if (foo) {
NSLog(@"cached foo is: %@", foo);
}
else {
foo = [FooObject new];
NSLog(@"new foo is: %@", foo);
[foo cacheForKey:key];
}
分类属性
在分类接口中声明属性与常规声明相同。
@interface FooObject (Category)
@property (nonatomic, strong) NSString *categoryString;
@end
然后使用运行时实现属性的setter和getter,并通过方法 - (void)registerPropertyName:(NSString *)propertyName
或 - (void)registerPropertyName:(NSString *)propertyName withCategoryName:(NSString *)categoryName
注册属性。
@implementation FooObject (Category)
- (void)setCategoryString:(NSString *)categoryString {
objc_setAssociatedObject(self, "categoryString", categoryString, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self registerPropertyName:@"categoryString"];
}
- (NSString *)categoryString {
return objc_getAssociatedObject(self, "categoryString");
}
@end
迁移
何时进行迁移?
- 更改属性名称。
- 更改属性类型。
- 同时更改属性名称和类型。
- 删除属性。
添加属性不需要迁移。
通用属性变更迁移
方法 - (BOOL)migrateWithData:(HMMigrationData *)migrationData fromVersion:(NSString *)version
用于从旧版本数据迁移到当前版本。默认情况下,使用 XCode 项目的版本作为 HMObject 子类的类版本。如果有多个版本已经发布,迁移代码必须按时间顺序编写。
- (BOOL)migrateWithData:(HMMigrationData *)migrationData fromVersion:(NSString *)version {
if ([version isEqualToString:@"1.0.0"]) {
// change name
[migrationData replaceKey:@"string" withKey:@"changeNameString"];
// change type
NSDate *date = [migrationData objectForKey:@"date"];
[migrationData setObject:@([date timeIntervalSince1970]) forKey:@"date"];
// delete
[migrationData removeObjectForKey:@"integer"];
// delete bar object, move barobject.string to fooobject.addString
HMMigrationData *barObject = [migrationData objectForKey:@"barObject"];
[migrationData removeObjectForKey:@"barObject"];
NSString *barObjectString = [barObject objectForKey:@"string"];
[migrationData setObject:barObjectString forKey:@"addString"];
}
if ([version isEqualToString:@"1.0.1"] ||
[version isEqualToString:@"1.0.2"] ||) {
// delete
[migrationData removeObjectForKey:@"array"];
}
...
return YES;
}
提示
- 返回 YES 表示迁移成功,而 NO 表示失败或放弃。返回 NO 将导致该版本的类模式从磁盘删除,因此无法为该版本进行未来的迁移操作。
- 如果从特定版本迁移没有需要执行的操作(例如,没有更改或仅添加了某些属性),则只需返回 YES。但是,如果您为旧版本实现过迁移代码,则不需要迁移的版本必须使用其前一版本的迁移代码。就像上面的迁移示例代码一样,版本 '1.0.1' 和 '1.0.2' 有相同的类模式,因此不需要从 '1.0.1' 迁移到 '1.0.2',但它们必须使用相同的迁移代码以迁移到/从其他版本。
删除 HMObject 子类的迁移
如果在特定版本之后已删除旧版本创建的 HMObject 子类,则它将在迁移方法 - (BOOL)migrateWithData:(HMMigrationData *)migrationData fromVersion:(NSString *)version
中被 HMMigrationData 实例替换。例如,FooObject 和 BarObject 都是 HMObject 的子类,FooObject 在版本 "1.0.0" 中拥有名为 "barObject" 的属性,类型为 BarObject,然后在版本 "1.0.1" 中删除了 BarObject。我们想要读取 "barObject" 属性的值并从 FooObject 中删除此键,这里是我们的做法
// Current version is "1.0.1"
- (BOOL)migrateWithData:(HMMigrationData *)migrationData fromVersion:(NSString *)version {
if ([version isEqualToString:@"1.0.0"]) {
HMMigrationData *barObject = migrationData[@"barObject"];
NSDate *barObjectDate = barObject[@"date"];
[migrationData replaceKey:@"barObject" withKey:@"barObjectDate" object:barObjectDate];
}
return YES;
}
很明显,整个 "barObject" 值已被另一个 HMMigrationData 替换,除了表示根 FooObject 实例的那个。在版本 "1.0.0" 中由 BarObject 拥有并由 barObject HMMigrationData 持有的属性 "date"。我们读取它并将旧的 "barObject" 键替换为新的键 "barObjectDate"。
描述
HMObject 重写了 NSObject 的 - (NSString *)description
和 - (NSString *)debugDescription
方法,以便在 XCode 控制台输出 JSON 格式的数据。它是一个子类,自动获得这种功能。以下是一个示例
- (void)testDescription {
NSLog(@"Testing description ... \n\n");
FooObject *foo = [FooObject new];
NSLog(@"new foo is: %@", foo);
NSMutableArray *mutableArray = [NSMutableArray array];
for (int i = 0; i < 3; i++) {
FooObject *object = [FooObject new];
[mutableArray addObject:object];
}
NSLog(@"array is: %@", mutableArray.description);
NSDictionary *dictionary = @{@"1" : [FooObject new],
@"2" : [SubFooObject new]};
NSLog(@"dictionary is: %@", dictionary.description);
}
在 XCode 控制台中输出的内容如下
2017-02-06 16:03:40.355 HMCacheDemo[10959:1016121] Testing description ...
2017-02-06 16:03:40.355 HMCacheDemo[10959:1016121] new foo is:
{
"*FooObject" : {
"(TestCategory)categoryString" : "This is a NSString in category",
"integer" : 1,
"barObjectDate" : null,
"string" : "I am a foo!"
}
}
2017-02-06 16:03:40.356 HMCacheDemo[10959:1016121] array is:
[
{
"*FooObject" : {
"(TestCategory)categoryString" : "This is a NSString in category",
"integer" : 1,
"barObjectDate" : null,
"string" : "I am a foo!"
}
},
{
"*FooObject" : {
"(TestCategory)categoryString" : "This is a NSString in category",
"integer" : 1,
"barObjectDate" : null,
"string" : "I am a foo!"
}
},
{
"*FooObject" : {
"(TestCategory)categoryString" : "This is a NSString in category",
"integer" : 1,
"barObjectDate" : null,
"string" : "I am a foo!"
}
}
]
2017-02-06 16:03:40.387 HMCacheDemo[10959:1016121] dictionary is:
{
"1" : {
"*FooObject" : {
"(TestCategory)categoryString" : "This is a NSString in category",
"integer" : 1,
"barObjectDate" : null,
"string" : "I am a foo!"
}
},
"2" : {
"*SubFooObject" : {
"number" : 123,
"**FooObject" : {
"(TestCategory)categoryString" : "This is a NSString in category",
"integer" : 1,
"barObjectDate" : null,
"string" : "I am a foo!"
}
}
}
}
对象或数组以标准的 JSON 文本格式打印出来。JSON 中的对象表示 HMObject 子类实例时,将被标注为 "*" 以区分表示 NSDictionary 实例的对象。在一个 HMObject 子类 JSON 对象内部,其超类(也是一种 HMObject)将被两个 "*" 标注以区分属性。
此外,类别属性也会被打印,并以方法注册的类别名称作为前缀
+ (void)registerPropertyName:(NSString *)propertyName withCategoryName:(NSString *)categoryName
.