Mantle是一个用于简化Cocoa或Cocoa Touch程序中model层的第三方库。通常我们的应该中都会定义大量的model来表示各种数据结构,而这些model的初始化和编码解码都需要写大量的代码。而Mantle的优点在于能够大大地简化这些代码。
Mantle源码中最主要的内容包括:
- MTLModel类:通常是作为我们的Model的基类,该类提供了一些默认的行为来处理对象的初始化和归档操作,同时可以获取到对象所有属性的键值集合。
- MTLJSONAdapter类:用于在MTLModel对象和JSON字典之间进行相互转换,相当于是一个适配器。
- MTLJSONSerializing协议:需要与JSON字典进行相互转换的MTLModel的子类都需要实现该协议,以方便MTLJSONApadter对象进行转换。
在此就以这三者作为我们的分析点。
基类MTLModel
MTLModel是一个抽象类,它主要提供了一些默认的行为来处理对象的初始化和归档操作。
初始化
MTLModel默认的初始化方法-init并没有做什么事情,只是调用了下[super init]。而同时,它提供了一个另一个初始化方法:
- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error;
其中参数dictionaryValue是一个字典,它包含了用于初始化对象的key-value对。我们来看下它的具体实现:
- (instancetype)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error {
...
for (NSString *key in dictionary) {
// 1. 将value标记为__autoreleasing,这是因为在MTLValidateAndSetValue函数中,
// 可以会返回一个新的对象存在在该变量中
__autoreleasing id value = [dictionary objectForKey:key];
// 2. value如果为NSNull.null,会在使用前将其转换为nil
if ([value isEqual:NSNull.null]) value = nil;
// 3. MTLValidateAndSetValue函数利用KVC机制来验证value的值对于key是否有效,
// 如果无效,则使用使用默认值来设置key的值。
// 这里同样使用了对象的KVC特性来将value值赋值给model对应于key的属性。
// 有关MTLValidateAndSetValue的实现可参考源码,在此不做详细说明。
BOOL success = MTLValidateAndSetValue(self, key, value, YES, error);
if (!success) return nil;
}
...
}
子类可以重写该方法,以在设置完对象的属性后做进一步的处理或初始化工作,不过需要记住的是:应该通过super来调用父类的实现。
获取属性的键(key)、值(value)
MTLModel类提供了一个类方法+propertyKeys,该方法返回所有@property声明的属性所对应的名称字符串的一个集合,但不包括只读属性和MTLModel自身的属性。在这个类方法会去遍历model的所有属性,如果属性是非只读且其ivar值不为NULL,则获取到表示属性名的字符串,并将其放入到集合中,其实现如下:
+ (NSSet *)propertyKeys {
// 1. 如果对象中已有缓存的属性名的集合,则直接返回缓存。该缓存是放在一个关联对象中。
NSSet *cachedKeys = objc_getAssociatedObject(self, MTLModelCachedPropertyKeysKey);
if (cachedKeys != nil) return cachedKeys;
NSMutableSet *keys = [NSMutableSet set];
// 2. 遍历对象所有的属性
// enumeratePropertiesUsingBlock方法会沿着superclass链一直向上遍历到MTLModel,
// 查找当前model所对应类的继承体系中所有的属性(不包括MTLModel),并对该属性执行block中的操作。
// 有关enumeratePropertiesUsingBlock的实现可参考源码,在此不做详细说明。
[self enumeratePropertiesUsingBlock:^(objc_property_t property, BOOL *stop) {
mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property);
@onExit {
free(attributes);
};
// 3. 过滤只读属性和ivar为NULL的属性
if (attributes->readonly && attributes->ivar == NULL) return;
// 4. 获取属性名字符串,并存储到集合中
NSString *key = @(property_getName(property));
[keys addObject:key];
}];
// 5. 将集合缓存到关联对象中。
objc_setAs