下面分析NSObject+YYModel
的实现,从头文件可以看该类由三个Category和一个Protocol所组成,其主要的实现集中在NSObject
的Category中。
_YYModelPropertyMeta
在其.m文件从上向下查看到的第一个类为_YYModelPropertyMeta
,实例化方法如下:
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
// 泛型类不存在且有遵守的协议,支持带协议名的伪泛型类
if (!generic && propertyInfo.protocols) {
for (NSString *protocol in propertyInfo.protocols) {
Class cls = objc_getClass(protocol.UTF8String);
if (cls) {
generic = cls;
break;
}
}
}
_YYModelPropertyMeta *meta = [self new];
// 属性名
meta->_name = propertyInfo.name;
// 属性类型
meta->_type = propertyInfo.type;
// 属性信息
meta->_info = propertyInfo;
// 泛型类
meta->_genericCls = generic;
// 属性类型为对象类型
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
// 属性的Foundation类型
meta->_nsType = YYClassGetNSType(propertyInfo.cls);
} else {
// 是否为C数字类型
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
}
// 结构体类型的属性
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
// NSKeyedUnarchiver不能decode除了下面这些结构体
static NSSet *types = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableSet *set = [NSMutableSet new];
// 32 bit
[set addObject:@"{CGSize=ff}"];
[set addObject:@"{CGPoint=ff}"];
[set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
[set addObject:@"{CGAffineTransform=ffffff}"];
[set addObject:@"{UIEdgeInsets=ffff}"];
[set addObject:@"{UIOffset=ff}"];
// 64 bit
[set addObject:@"{CGSize=dd}"];
[set addObject:@"{CGPoint=dd}"];
[set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
[set addObject:@"{CGAffineTransform=dddddd}"];
[set addObject:@"{UIEdgeInsets=dddd}"];
[set addObject:@"{UIOffset=dd}"];
types = set;
});
// 属性类型的编码值属于上面的结构体
if ([types containsObject:propertyInfo.typeEncoding]) {
// 能够archiver的结构体
meta->_isStructAvailableForKeyedArchiver = YES;
}
}
// 属性类
meta->_cls = propertyInfo.cls;
if (generic) {
meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
} else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
}
// 取值方法
if (propertyInfo.getter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
meta->_getter = propertyInfo.getter;
}
}
// 设值方法
if (propertyInfo.setter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
meta->_setter = propertyInfo.setter;
}
}
if (meta->_getter && meta->_setter) {
// 不能KVC的类型
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock:
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
meta->_isKVCCompatible = YES;
} break;
default: break;
}
}
return meta;
}
大致就是将YYClassPropertyInfo
中的属性作了一个转换,获取属性的Foundation和是否为C数字的函数较为简单,在实现文件中,所有的函数前都加了force_inline
,这是作者定义的宏。
#define force_inline __inline__ __attribute__((always_inline))
这个宏的意思是这样的:
attribute((always_inline))的意思是强制内联,所有加了attribute((always_inline))的函数再被调用时不会被编译成函数调用而是直接扩展到调用函数体内,比如我定义了函数
attribute((always_inline)) void a()和
void b()
{
a();
}
b调用a函数的汇编代码不会是跳转到a执行,而是a函数的代码直接在b内成为b的一部分。
也可上Stack Overflow查看更专业的回答。
YYModel Protocol
协议包含了八个可选实现方法,八个方法的使用在NSObject+YYModel.h
中有详细描述,这里只是简单介绍一下。
-
+ (NSDictionary *)modelCustomPropertyMapper
定制model的属性与设置值的对应关系; -
+ (NSDictionary *)modelContainerPropertyGenericClass
Model的容器类属性,如:NSDictionary, NSArray, NSSet等对应的泛型类; -
+ (Class)modelCustomClassForDictionary:(NSDictionary *)dictionary
在JSON -> Model的转换中,用该方法选择基于字典数据的自定义类; -
+ (NSArray *)modelPropertyBlacklist
model属性黑名单,位于黑名单中的属性将不会被赋值,返回nil忽略此特性; -
+ (NSArray *)modelPropertyWhitelist
model属性白名单,不位于白名单中的属性将不会被赋值,返回nil忽略此特性; -
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic
如果Model实现了此方法,它将会在+modelWithJSON:
+modelWithDictionary:
-modelSetWithJSON:
-modelSetWithDictionary:
之前调用,如果该方法返回空,model将会忽略; -
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic
如果默认的json-to-model不适合model对象,可以实现此方法,会在+modelWithJSON:
+modelWithDictionary:
-modelSetWithJSON:
-modelSetWithDictionary:
之后调用;返回YES表明此model可用,返回NO忽略此model; -
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic
如果默认的model-to-json不适合model类,可以实现此方法,会在-modelToJSONObject
-modelToJSONString
之后调用;返回YES表明此model可用,返回NO忽略此model;
_YYModelMeta
不多说,直接上源码开始分析:
- (instancetype)initWithClass:(Class)cls {
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
if (!classInfo) return nil;
self = [super init];
// 黑名单列表
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// 白名单列表
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
// 容器类属性的泛型类
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
if (genericMapper) {
NSMutableDictionary *tmp = [NSMutableDictionary new];
[genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (![key isKindOfClass:[NSString class]]) return;
Class meta = object_getClass(obj);
if (!meta) return;
if (class_isMetaClass(meta)) {
tmp[key] = obj;
} else if ([obj isKindOfClass:[NSString class]]) {
Class cls = NSClassFromString(obj);
if (cls) {
tmp[key] = cls;
}
}
}];
genericMapper = tmp;
}
}
// 创建所有属性的元信息
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
// 递归解析父类,忽略根类(NSObject/NSProxy)
while (curClassInfo && curClassInfo.superCls != nil) {
for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
// 属性名不存在
if (!propertyInfo.name) continue;
// 黑名单存在且此属性名位于黑名单
if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
// 白名单存在且此属性名不位于白名单
if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
if (!meta || !meta->_name) continue;
if (!meta->_getter || !meta->_setter) continue;
if (allPropertyMetas[meta->_name]) continue;
allPropertyMetas[meta->_name] = meta;
}
curClassInfo = curClassInfo.superClassInfo;
}
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
// 映射关系表
NSMutableDictionary *mapper = [NSMutableDictionary new];
// keyPath -> model property 映射数组
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
// 多个key -> model property 映射数组
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
// 定制的 key -> property 映射关系表
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
// 取出属性元信息
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return;
// 遍历一个删除一个
[allPropertyMetas removeObjectForKey:propertyName];
// key为字符串的情况,还有一种为数组,即多个key对应一个property
if ([mappedToKey isKindOfClass:[NSString class]]) {
if (mappedToKey.length == 0) return;
propertyMeta->_mappedToKey = mappedToKey;
NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
// 去除空格
for (NSString *onePath in keyPath) {
if (onePath.length == 0) {
NSMutableArray *tmp = keyPath.mutableCopy;
[tmp removeObject:@""];
keyPath = tmp;
break;
}
}
if (keyPath.count > 1) {
propertyMeta->_mappedToKeyPath = keyPath;
[keyPathPropertyMetas addObject:propertyMeta];
}
// 多个property对应一个key,使用_next串起来
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
// key 数组
NSMutableArray *mappedToKeyArray = [NSMutableArray new];
for (NSString *oneKey in ((NSArray *)mappedToKey)) {
if (![oneKey isKindOfClass:[NSString class]]) continue;
if (oneKey.length == 0) continue;
NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
if (keyPath.count > 1) {
[mappedToKeyArray addObject:keyPath];
} else {
[mappedToKeyArray addObject:oneKey];
}
if (!propertyMeta->_mappedToKey) {
propertyMeta->_mappedToKey = oneKey;
propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
}
}
if (!propertyMeta->_mappedToKey) return;
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
[multiKeysPropertyMetas addObject:propertyMeta];
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
}];
}
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
propertyMeta->_mappedToKey = name;
propertyMeta->_next = mapper[name] ?: nil;
mapper[name] = propertyMeta;
}];
if (mapper.count) _mapper = mapper;
if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
_classInfo = classInfo;
_keyMappedCount = _allPropertyMetas.count;
_nsType = YYClassGetNSType(cls);
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
return self;
}
- 查看有没有实现黑名单和白名单列表,如果有的话记录下来;
- 容器类属性的泛型类要求,处理好记录下来;
- model类所有的属性元信息,包括父类,忽略根类(NSObject/NSProxy);
- 定制的key->property映射关系;
- 其他简单的赋值处理。
对于定制的key->property映射关系稍微讲解一下,例如对下面的model类来说:
@interface YYBook : NSObject<YYModel>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *desc;
@property (nonatomic, copy) NSString *bookID;
@property (nonatomic, assign) NSInteger page;
@end
对应的返回json数据如下:
{
"n":"Harry Pottery",
"p": 256,
"ext" : {
"desc" : "A book written by J.K.Rowling."
},
"ID" : 100010
}
默认情况下key和property是相同名称,一一对应关系的。如上的情况,返回的字典的key与model的属性名不相同这样赋值的时由于找不到model对应的property会导致赋值失败,所以我们需要定制映射关系。
+ (NSDictionary<NSString *,id> *)modelCustomPropertyMapper {
return @{@"name" : @"n",
@"page" : @"p",
@"desc" : @"ext.desc",
@"bookID": @[@"id", @"ID", @"book.id"]};
}
model类遵守YYModel
协议,实现上述方法,返回key->property映射关系字典。
YYModel
在处理映射关系字典时分了几种情况:
- key直接对应property;
- keyPath对应property,这种情况下会进行
.
分割、去空字符处理,将分割后的数组记录下来用于后续赋值操作; - property对应多个key。