1:<code>YYClassInfo</code> 实现了跟Class 以及属性相关的类,因为runtime 中 这些都是以结构体形式表型的,所以作者封装了一下,具体有实现了
- YYClassIvarInfo 对应 Ivar
- YYClassMethodInfo 对应 Method
- YYClassPropertyInfo 对应 objc_property_t
- YYClassInfo 对应 Class
2:NSObject+YYModel 文件中实现了一些分类,这样子只要引入头文件就能调用方法了。
- NSObject (YYModel)
- NSArray (YYModel)
- NSDictionary (YYModel)
YYEncodingGetType 根据 typeEncoding 获取 类型的方法,
依旧从栗子开始跑代码。
@interface YYBook : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) uint64_t pages;
@property (nonatomic, strong) NSDate *publishDate;
@end
@implementation YYBook
@end
static void SimpleObjectExample() {
YYBook *book = [YYBook modelWithJSON:@" \
{ \
\"name\": \"Harry Potter\", \
\"pages\": 512, \
\"publishDate\": \"2010-01-01\" \
}"];
NSString *bookJSON = [book modelToJSONString];
NSLog(@"Book: %@", bookJSON);
}
Demo中实现了一个简单的Model YYBook ,然后用一个Json字符串进行初始化。我们重点看一下初始化的过程。
+ (instancetype)modelWithJSON:(id)json {
/// 返回json字典
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
/// 生成Model
return [self modelWithDictionary:dic];
}
<code>_yy_dictionaryWithJSON</code> 根据传入的json 返回一个字典,<code>json</code>可以是 NSDictionary NSString NSData
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
<code>_methodInfos</code>最后存储的如下:
{
".cxx_destruct" = "<YYClassMethodInfo: 0x600000265540>";
name = "<YYClassMethodInfo: 0x60000027c340>";
pages = "<YYClassMethodInfo: 0x600000267780>";
publishDate = "<YYClassMethodInfo: 0x600000265740>";
"setName:" = "<YYClassMethodInfo: 0x60000027dd00>";
"setPages:" = "<YYClassMethodInfo: 0x600000266840>";
"setPublishDate:" = "<YYClassMethodInfo: 0x600000265700>";
}
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
_propertyInfos 存储的如下:
{
name = "<YYClassPropertyInfo: 0x608000291940>";
pages = "<YYClassPropertyInfo: 0x6000002892e0>";
publishDate = "<YYClassPropertyInfo: 0x600000286630>";
}
_ivarInfos 存储的如下:
{
"_name" = "<YYClassIvarInfo: 0x60800044a170>";
"_pages" = "<YYClassIvarInfo: 0x60800024ad10>";
"_publishDate" = "<YYClassIvarInfo: 0x60800044a7d0>";
}
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
/// 获取 Class 的信息
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
/// 用户有没有实现指定的类型的方法
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
/// 生成 Model
NSObject *one = [cls new];
if ([one modelSetWithDictionary:dictionary]) return one;
return nil;
}
<code>
_YYModelMeta
/code>是内建的Model,<code>
metaWithClass
</code>方法,会生成 _YYModelMeta 对象,因为Class 有可能经常使用,所以进行了缓存,
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef cache;
static dispatch_once_t onceToken;
/// 线程安全
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
dispatch_semaphore_signal(lock);
///缓存中没有
if (!meta || meta->_classInfo.needUpdate) {
/// 从新创建一个
meta = [[_YYModelMeta alloc] initWithClass:cls];
if (meta) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
dispatch_semaphore_signal(lock);
}
}
return meta;
}
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
if (modelMeta->_keyMappedCount == 0) return NO;
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
/// 设置属性方法
/// 生成一个遍历的上下文环境,在 CFDictionaryApplyFunction 中使用
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
if (modelMeta->_keyPathPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_multiKeysPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
///property的属性,然后赋值
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
__unsafe_unretained id model = (__bridge id)(context->model);
while (propertyMeta) {
if (propertyMeta->_setter) {
///通过 setter方法 设置property的值
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
/// 之前设置属性的时候设置过next 方便这里循环
propertyMeta = propertyMeta->_next;
};
}
<code>
ModelSetValueForProperty
</code>方法中会根据Property 的类型的不同,然后调用不同的 <code>objc_msgSend</code>函数,比如:
if ([value isKindOfClass:[NSString class]]) {
if (meta->_nsType == YYEncodingTypeNSString) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);
}
property 类型是NSString 就会调用对用的setter方法,但是setter方法要强转转为对应的函数指针
如果实现了协议的<code>modelCustomPropertyMapper</code>方法,如下:
@implementation MsgDispatchInfo
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"bindID" : @"id"};
}
@end
网路请求返回的json中,key使用的是id model中要使用bindID就实现上面那个方法。然后,对应起来就好。
这里会在 <code>_YYModelMeta</code>的 初始化方法中用到。实现代码如下。
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
///获取到用户实现的字典
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
///如果class 本身没有 property 直接返回,比如 bindID 根本没有那个属性的时候
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return;
[allPropertyMetas removeObjectForKey:propertyName];
if ([mappedToKey isKindOfClass:[NSString class]]) {
if (mappedToKey.length == 0) return;
///记录要使用的key 上面栗子中的 id
propertyMeta->_mappedToKey = mappedToKey;
///如果对应的值不是id 比如 name.id
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];
}
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
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;
}
}];
}
这样子走完之后会在下面赋值的时候判断。在<code>yy_modelSetWithDictionary</code>方法中会判断,实现如下:
if (modelMeta->_keyPathPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
CFArrayApplyFunction() 和 CFDictionaryApplyFunction() 用来遍历字典和数组。
如果实现了<code>modelContainerPropertyGenericClass</code>方法。比如:
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"photos" : YYPhoto.class,
@"likedUsers" : YYUser.class,
@"likedUserIds" : NSNumber.class};
}
这里 key值都是集合,比如Demo中的
@interface YYAlbum : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSArray *photos; // Array<YYPhoto>
@property (nonatomic, strong) NSDictionary *likedUsers; // Key:name(NSString) Value:user(YYUser)
@property (nonatomic, strong) NSSet *likedUserIds; // Set<NSNumber>
@end
同样会在初始化<code>_YYModelMeta</code>的时候,做出处理,在以后赋值的时候使用。具体如下:
// Get container property's generic class
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;
}
}
会生成一个键值对应的字典,<code>Demo</code>中生成的字典如下:
{
likedUserIds = NSNumber;
likedUsers = YYUser;
photos = YYPhoto;
}
然后生成<code>_YYModelPropertyMeta</code>的时候会查询上面那个字典。如下。
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
在初始化的时候会进行记录
meta->_genericCls = generic;
最后调用<code>objc_msgSend</code>时候,判断属性的类型是不是集合,如果是集合,集合里的元素,就通过<code>_genericCls</code>生成,
网友评论