YYWebImage源码分析

YYImage源码

YYText源码分析

框架简介

YYClassIvarInfo

此类就是objc_ivar的封装

/**Instance variable information.struct objc_ivar {char * _Nullable ivar_name                               OBJC2_UNAVAILABLE;char * _Nullable ivar_type                               OBJC2_UNAVAILABLE;int ivar_offset                                          OBJC2_UNAVAILABLE;#ifdef __LP64__int space                                                OBJC2_UNAVAILABLE;#endif}*/
@interface YYClassIvarInfo : NSObject
@property (nonatomic, assign, readonly) Ivar ivar;              ///< ivar opaque struct 变量 objc_ivar
@property (nonatomic, strong, readonly) NSString *name;         ///< Ivar's name 变量名称  ivar_name
@property (nonatomic, assign, readonly) ptrdiff_t offset;       ///< Ivar's offset 变量偏移 ivar_offset
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding 变量类型编码 ivar_getTypeEncoding
@property (nonatomic, assign, readonly) YYEncodingType type;    ///< Ivar's type 变量类型 YYEncodingGetType 

YYClassMethodInfo

此类就是objc_method的封装

/**Method information.struct objc_method {SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;char * _Nullable method_types                            OBJC2_UNAVAILABLE;IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;}*/
@interface YYClassMethodInfo : NSObject
@property (nonatomic, assign, readonly) Method method;                  ///< method opaque struct   方法
@property (nonatomic, strong, readonly) NSString *name;                 ///< method name    方法名称
@property (nonatomic, assign, readonly) SEL sel;                        ///< method's selector 方法选择器  KEY
@property (nonatomic, assign, readonly) IMP imp;                        ///< method's implementation 方法实现 value
@property (nonatomic, strong, readonly) NSString *typeEncoding;         ///< method's parameter and return types 方法参数和编码
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding;   ///< return value's type 返回值类型编码
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type 参数编码数组

YYClassPropertyInfo

此类就是objc_property_attribute_t的封装

/**Property information./// Defines a property attributetypedef struct {const char * _Nonnull name;
const char * _Nonnull value;
} objc_property_attribute_t;*/
@interface YYClassPropertyInfo : NSObject
@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct 属性
@property (nonatomic, strong, readonly) NSString *name;           ///< property's name 属性名称
@property (nonatomic, assign, readonly) YYEncodingType type;      ///< property's type 属性类型
@property (nonatomic, strong, readonly) NSString *typeEncoding;   ///< property's encoding value 属性类型编码
@property (nonatomic, strong, readonly) NSString *ivarName;       ///< property's ivar name 变量名称
@property (nullable, nonatomic, assign, readonly) Class cls;      ///< may be nil 属性类型
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil 属性相关协议
@property (nonatomic, assign, readonly) SEL getter;               ///< getter (nonnull)
@property (nonatomic, assign, readonly) SEL setter;               ///< setter (nonnull)

YYClassInfo

以上三个类的集合就是这个类,而且这个类也是objc_class的封装

/**Class information for a class.struct objc_class {Class _Nonnull isa  OBJC_ISA_AVAILABILITY;Class _Nullable super_class                              OBJC2_UNAVAILABLE;const char * _Nonnull name                               OBJC2_UNAVAILABLE;struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;#endif} OBJC2_UNAVAILABLE;在objc中,id代表了一个对象。根据上面的声明,凡是首地址是*isa的struct指针,都可以被认为是objc中的对象。运行时可以通过isa指针,查找到该对象是属于什么类(Class)。*/
@interface YYClassInfo : NSObject
@property (nonatomic, assign, readonly) Class cls; ///< class object  类
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object 超类
@property (nullable, nonatomic, assign, readonly) Class metaCls;  ///< class's meta class object 元类
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class 是否是元类
@property (nonatomic, strong, readonly) NSString *name; ///< class name 类名
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info 超类Class的对象
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars 变量字典
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods 方法字典
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties 属性字典

以下就是YYClassInfo类的核心初始化方法

  1. class_copyMethodList -->Method  -->YYClassMethodInfo
  2. class_propertyList--->objc_property --> YYClassProperyInfo
  3. class_copyIvarList--->Ivar---> YYClassIvarInfo
- (void)_update {_ivarInfos = nil;_methodInfos = nil;_propertyInfos = nil;Class cls = self.cls;unsigned int methodCount = 0;Method *methods = class_copyMethodList(cls, &methodCount);if (methods) {NSMutableDictionary *methodInfos = [NSMutableDictionary new];_methodInfos = methodInfos;for (unsigned int i = 0; i < methodCount; i++) {YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];if (info.name) methodInfos[info.name] = info;}free(methods);}unsigned int propertyCount = 0;objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);if (properties) {NSMutableDictionary *propertyInfos = [NSMutableDictionary new];_propertyInfos = propertyInfos;for (unsigned int i = 0; i < propertyCount; i++) {YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];if (info.name) propertyInfos[info.name] = info;}free(properties);}unsigned int ivarCount = 0;Ivar *ivars = class_copyIvarList(cls, &ivarCount);if (ivars) {NSMutableDictionary *ivarInfos = [NSMutableDictionary new];_ivarInfos = ivarInfos;for (unsigned int i = 0; i < ivarCount; i++) {YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];if (info.name) ivarInfos[info.name] = info;}free(ivars);}if (!_ivarInfos) _ivarInfos = @{};if (!_methodInfos) _methodInfos = @{};if (!_propertyInfos) _propertyInfos = @{};_needUpdate = NO;
}

NSObject+YYModel

通过扩展Categroy来实现YYModel的各种功能

  1. _YYModelMeta    ---> YYClassInfo

  2. _YYModelPropertyMeta   ---> YYClassPropertyInfo

介绍基本的结构,下面根据调用顺序走一遍流程 Demo是大佬自己的Demo 点击打开链接 可以边看边研究内部知识点

1.json-model 外部接口(YYModel Demo 微博数据为例)

[YYWeiboStatus yy_modelWithJSON:json];

这里的json内部做了兼容,无论是字符串还是NSData都可以接受,或者自己转换完了调用下面的也行

+ (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;
+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {if (!dictionary || dictionary == (id)kCFNull) return nil;if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;Class cls = [self class];_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];if (modelMeta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;}NSObject *one = [cls new];if ([one yy_modelSetWithDictionary:dictionary]) return one;return nil;
}

2._YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];

这里就是生成核心类YYModelMeta

1.初始化类的所有信息 YYClassInfo

2.生成Mapper  (json(key) -- value (YYModelPropertyMeta))

首先看下这个工厂方法

/// Returns the cached model class meta
// 先进行Meta class初始化 先取缓存 CFMutableDictionaryRef cach
// _YYModelMeta 表示模型的类信息,它包含 YYClassInfo。
+ (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) {// 创建之后进行加锁存储 和上面对应  CF下面的字典也是非线程安全需要加锁操作dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));dispatch_semaphore_signal(lock);}}return meta;
}

无论是YYModelMeta还是YYClassInfo的获取方式基本都是类似的代码,这里就按这个稍微分析下

这里通过dispatch_once生成了一个静态的Cache容器 和 初始化了信号量锁dispatch_semaphore_create 用法介绍

然后通过Class为key去Cache中取,这个过程是有信号量锁进行线程安全的保证,读取到了就返回,没有读取到,初始化

YYModelMeta

- (instancetype)initWithClass:(Class)cls {// 获取类信息 有缓存读缓存,没有缓存重新构建YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];if (!classInfo) return nil;self = [super init];// Get black list// Get white list// Get container property's generic class
//    + (NSDictionary *)modelContainerPropertyGenericClass {
//        return @{@"picIds" : [NSString class],
//                 @"picInfos" : [YYWeiboPicture class],
//                 @"urlStruct" : [YYWeiboURL 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]]) {// @"picInfos" : @"YYWeiboPicture" 这是第二种写法Class cls = NSClassFromString(obj);if (cls) {tmp[key] = cls;}}}];// @"picInfos" : [YYWeiboPicture class]genericMapper = tmp;}}// Create all property metas.// {name :_YYModelPropertyMeta(YYClassPropertyInfo变化而来)}  用来Mapper过滤NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];YYClassInfo *curClassInfo = classInfo;while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)// 所有属性字典 propertyInfosfor (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {if (!propertyInfo.name) continue;if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;// 根据YYClassInfo中的Property字典,遍历拿出来给_YYModelPropertyMeta赋值  宓珂璟  这里属性的类型_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfopropertyInfo:propertyInfogeneric: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;// create mapper// 核心Key - value 针对外部json数据的转换字典,之前在YYClassInfo是根据runtime读取到的类中的属性作为key,如果和Json不同,需要重新map 然后重新根据key指定对应的YYPropertyInfoNSMutableDictionary *mapper = [NSMutableDictionary new];NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];//    key 是 对象   value是json
//    + (NSDictionary *)modelCustomPropertyMapper {
//        return @{@"cutType" : @"cut_type"};
//    }// 这个If是针对属性和json数据不一致进行过滤mapperif ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];if (!propertyMeta) return;// 把重写的Maper的key从原始字典中剔除[allPropertyMetas removeObjectForKey:propertyName];if ([mappedToKey isKindOfClass:[NSString class]]) {if (mappedToKey.length == 0) return;// 重新指定mappedKey return @{@"cutType" : @"cut_type"};// 如果重写 modelCustomPropertyMapper  那么mappedKey不在是属性cutType,而是cut_typepropertyMeta->_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];}propertyMeta->_next = mapper[mappedToKey] ?: nil;// mapper指定key - valuemapper[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;}}];}// 上面的属性和json字段不一致,进过过滤 mapper存储// 现在把过滤剩下的字典进行遍历 这里的mappedKey就是属性的name[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {propertyMeta->_mappedToKey = name;propertyMeta->_next = mapper[name] ?: nil;mapper[name] = propertyMeta;}];/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
//    NSDictionary *_mapper;// mapper赋值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;
}

1.第一步获取YYClassInfo 还是和_YYModelMeta一样都会先读取缓存,有的话返回,没有就初始化,这里的初始化参数可以看上面的YYClassInfo接收

2.黑白名单过滤(没实现代理方法可以忽略)

3.代理方法 modelContainerPropertyGenericClass  如果是容器属性例如  NSArray/NSSet/NSDictionay,需要通过该代理方法指定对应容器是什么类型
不给的话就默认就是属性的类型例如
+ (NSDictionary *)modelContainerPropertyGenericClass {
    return @{@"picIds" : [NSString class],
             @"picInfos" : [YYWeiboPicture class],
             @"urlStruct" : [YYWeiboURL class]};
}
生成一个genericMapper 的容器属性类型字典

4.根据YYClassInfo里面的propertyInfos<YYClassPropertyInfo>属性容器生成_YYModelPropertyMeta个体,然后放进数组,给_allPropertyMetas赋值

5.生成mapper
如果Json的key和类属性值不同,我们就需要实现modelCustomPropertyMapper代理方法进行映射
先从上面生成的allPropertyMetas字典  (key(name)-----value(_YYModelPropertyMeta)) 根据 需要映射的Key读取出需要映射的_YYModelPropertyMeta
如果通过Key能拿到,说明,这个key是属性的key,不是json数据的key,我们需要把key替换成json数据的key,然后从allPropertyMetas中剔除取到的key value
这里先不管key-path的情况,重新填充到生成的临时Mapper   mapper[mappedToKey] = propertyMeta; 这里的mappedKey不再是属性的key,而是我们实现代理方法里面对应的value作为key,生成的Mapper,随后,把allPropertyMetas剩下的值重新拿填充mapperKey,放入Mapper字典中,然后赋值给 _mapper 生成新的最新的Mapper(这些操作前提是属性的key和json的key对不上)

以上YYModelMeta创建成功就加锁,还是以Class为key 调用 CFDictionarySetValue存储到静态的cache容器中缓存起来

3.yy_modelSetWithDictionary

- (BOOL)yy_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;}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;
}

这里方法都会再次调用_YYModelMeta 由于上面的缓存策略,直接从静态cache根据Class读取出来,这也是YYModel性能较高的原因之一

ModelSetContext这个结构体

  • void *modelMeta;  ///< _YYModelMeta
  • void *model;      ///< id (self)
  • void *dictionary; ///< NSDictionary (json)

调用核心Map方法

CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);

参数1:Json数据

参数2:typedef void (*CFDictionaryApplierFunction)(const void *key, const void *value, void *context); 类型函数

参数3:ModelSetContext结构体

/**Apply function for dictionary, to set the key-value pair to model.@param _key     should not be nil, NSString.@param _value   should not be nil.@param _context _context.modelMeta and _context.model should not be nil.*/
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {ModelSetContext *context = _context;__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];__unsafe_unretained id model = (__bridge id)(context->model);while (propertyMeta) {if (propertyMeta->_setter) {// model  模型对象 self// value  json value// propertyMeta   _YYModelPropertyMeta (属性对象信息)ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);}propertyMeta = propertyMeta->_next;};
}

4.ModelSetValueForProperty (objc_ msgSend最终赋值)

static void ModelSetValueForProperty(__unsafe_unretained id model,__unsafe_unretained id value,__unsafe_unretained _YYModelPropertyMeta *meta) {if (meta->_isCNumber) { // 1NSNumber *num = YYNSNumberCreateFromID(value);ModelSetNumberToProperty(model, num, meta);if (num) [num class]; // hold the number} else if (meta->_nsType) { // 2if (value == (id)kCFNull) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);} else {switch (meta->_nsType) {case YYEncodingTypeNSString:case YYEncodingTypeNSMutableString: {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);}} else if ([value isKindOfClass:[NSNumber class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,(meta->_nsType == YYEncodingTypeNSString) ?((NSNumber *)value).stringValue :((NSNumber *)value).stringValue.mutableCopy);} else if ([value isKindOfClass:[NSData class]]) {NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding];((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string);} else if ([value isKindOfClass:[NSURL class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,(meta->_nsType == YYEncodingTypeNSString) ?((NSURL *)value).absoluteString :((NSURL *)value).absoluteString.mutableCopy);} else if ([value isKindOfClass:[NSAttributedString class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,(meta->_nsType == YYEncodingTypeNSString) ?((NSAttributedString *)value).string :((NSAttributedString *)value).string.mutableCopy);}} break;case YYEncodingTypeNSArray:case YYEncodingTypeNSMutableArray: {if (meta->_genericCls) {NSArray *valueArr = nil;if ([value isKindOfClass:[NSArray class]]) valueArr = value;else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects;if (valueArr) {NSMutableArray *objectArr = [NSMutableArray new];for (id one in valueArr) {if ([one isKindOfClass:meta->_genericCls]) {[objectArr addObject:one];} else if ([one isKindOfClass:[NSDictionary class]]) {// 递归模型转换  key : [{},{},{}]Class cls = meta->_genericCls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:one];if (!cls) cls = meta->_genericCls; // for xcode code coverage}NSObject *newOne = [cls new];[newOne yy_modelSetWithDictionary:one];if (newOne) [objectArr addObject:newOne];}}// 赋值((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);}} else {if ([value isKindOfClass:[NSArray class]]) {if (meta->_nsType == YYEncodingTypeNSArray) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,((NSArray *)value).mutableCopy);}} else if ([value isKindOfClass:[NSSet class]]) {if (meta->_nsType == YYEncodingTypeNSArray) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,((NSSet *)value).allObjects.mutableCopy);}}}} break;case YYEncodingTypeNSDictionary:case YYEncodingTypeNSMutableDictionary: {if ([value isKindOfClass:[NSDictionary class]]) {if (meta->_genericCls) {NSMutableDictionary *dic = [NSMutableDictionary new];[((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) {if ([oneValue isKindOfClass:[NSDictionary class]]) {Class cls = meta->_genericCls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:oneValue];if (!cls) cls = meta->_genericCls; // for xcode code coverage}NSObject *newOne = [cls new];[newOne yy_modelSetWithDictionary:(id)oneValue];if (newOne) dic[oneKey] = newOne;}}];((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic);} else {if (meta->_nsType == YYEncodingTypeNSDictionary) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,((NSDictionary *)value).mutableCopy);}}}} break;default: break;}}} else {// 3BOOL isNull = (value == (id)kCFNull);switch (meta->_type & YYEncodingTypeMask) {case YYEncodingTypeObject: {if (isNull) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);} else if ([value isKindOfClass:meta->_cls] || !meta->_cls) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);} else if ([value isKindOfClass:[NSDictionary class]]) {NSObject *one = nil;if (meta->_getter) {one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);}if (one) {// 宓珂璟 如何类型是我们自己定义的类型, 跑进来,直接赋值  第一次nil[one yy_modelSetWithDictionary:value];} else {// 递归调用直接赋值 子Json  宓珂璟Class cls = meta->_cls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:value];if (!cls) cls = meta->_genericCls; // for xcode code coverage}one = [cls new];[one yy_modelSetWithDictionary:value];((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);}}} break;default: break;}}
}

分析之前先看下_nsType是怎么赋值的

/// Get the Foundation class type from property info.
static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {if (!cls) return YYEncodingTypeNSUnknown;if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;return YYEncodingTypeNSUnknown;
}

可以在源码中看到,所有的属性类型会变成YY自定义的YY类型,而且如果NSType类型都有自己对应的YYEncoding类型,但是如果你是自定义属性类型,比如这样

@property (nonatomic, strong) YYWeiboUser *user;

那么这个时候你的_nsType类型就是YYEncodingTypeNSUnknown,对应的值是0,OK,下面接着分析最后的赋值函数

上面那一大坨函数标记了1,2,3,

第一步,判断是的是否是_CNumber是否是基本数据类型

第二步,判断 meta->_nsType 就是代表不是自定义类型,是NSType类型的,这里以NSArray,NSDictionary,NSString为例子

  • NSString 调用 函数直接setter赋值
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
  • NSArray

                       if (meta->_genericCls) {if (valueArr) {NSMutableArray *objectArr = [NSMutableArray new];for (id one in valueArr) {if ([one isKindOfClass:meta->_genericCls]) {[objectArr addObject:one];} else if ([one isKindOfClass:[NSDictionary class]]) {// 递归模型转换  key : [{},{},{}]Class cls = meta->_genericCls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:one];if (!cls) cls = meta->_genericCls; // for xcode code coverage}NSObject *newOne = [cls new];[newOne yy_modelSetWithDictionary:one];if (newOne) [objectArr addObject:newOne];}}// 赋值((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);}} else {if ([value isKindOfClass:[NSArray class]]) {if (meta->_nsType == YYEncodingTypeNSArray) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,((NSArray *)value).mutableCopy);}} }

省略了部分代码,注意,这里一进来就判断_genericCls 这个类型在上面已经提到,是通过
    modelContainerPropertyGenericClass 方法设置的容器对应的内容类型,然后根据数组里面字典的类型,递归继续调用
    yy_modelSetWithDictionary,递归调用之后,通过objc_msgSend把数组赋值给self的对应属性,如果没有实现代理方法指        定容器的类型Class,那么Json返回的也是数组,我们的类型也是数组,直接赋值即可

  • NSDictionary这里的操作和数组基本一致,由于都是由指定容器类型,这里的递归,就不需要数组遍历,把对应的json字典再转一次,进行赋值即可,都在上面一坨长代码上有显示,已经把多余的删掉了

这是第二步实现代码的属性写法

@property (nonatomic, strong) NSArray *picIds;        /// Array<NSString>
@property (nonatomic, strong) NSDictionary *picInfos; /// Dic<NSString, YYWeiboPicture>
@property (nonatomic, strong) NSArray *urlStruct;     ///< Array<YYWeiboURL>
+ (NSDictionary *)modelContainerPropertyGenericClass {return @{@"picIds" : [NSString class],@"picInfos" : [YYWeiboPicture class],@"urlStruct" : [YYWeiboURL class]};
}

可以看出,这里的属性都是NSType类型的,而且实现了代理方法,指定了容器的对应类型,如果是数组,那么数组里面都是该类型的对象,如果是字典,例如picInfos,这里给的类型是NSDictionary的,因此,后面无论指定什么模型,最终返回的就是一个字典类型

第三步NSType是YYEncodingTypeNSUnknown类型(自定义Class)

     switch (meta->_type & YYEncodingTypeMask) {case YYEncodingTypeObject: {if (isNull) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);} else if ([value isKindOfClass:meta->_cls] || !meta->_cls) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);} else if ([value isKindOfClass:[NSDictionary class]]) {NSObject *one = nil;if (meta->_getter) {one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);}if (one) {// 宓珂璟 如何类型是我们自己定义的类型, 跑进来,直接赋值  第一次nil[one yy_modelSetWithDictionary:value];} else {// 递归调用直接赋值 子Json  宓珂璟Class cls = meta->_cls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:value];if (!cls) cls = meta->_genericCls; // for xcode code coverage}one = [cls new];[one yy_modelSetWithDictionary:value];((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);}}} break;default: break;}

这里的分支主要是给以下这种属性使用的,由于不是数组,一个字典类型,也就是一个模型,可以直接给出模型的类型声明即可

@property (nonatomic, strong) YYWeiboUser *user;

这种写法更简洁一点

总结

知识点1:Runtime的方法获取类的属性,方法,变量,包装成YYClassInfo

知识点2:读取Class信息的时候通过静态容器缓存到内存,只有needUpdate开关开启才更新缓存,提高转换性能

知识点3:缓存中的CF字典非线程安全,通过信号量锁进行线程安全操作

知识点4:各种类型转换和类型安全(这个源码中很详细,很吊)

知识点5:Model类中实现代理方法实现用户自定义模型转换

知识点6:force_inline强制内联函数(函数再被调用时不会被编译成函数调用而是直接扩展到调用函数体内,编译期间类似宏直接替换插入进去,牺牲内存,带来性能优化)

知识点7:可以看到最终赋值通过objc_msgSend以及Setter的SEL进行,Key-Value Coding 使用起来非常方便,但性能上要差于直接调用 Getter/Setter,所以如果能避免 KVC 而用 Getter/Setter 代替,性能会有较大提升。
而且访问变量的时候用ivar,避免使用getter和setter

知识点8:使用CoreFoundation下的函数遍历容器,例如 CFDictionaryApplyFunction

知识点9:__unsafe_unretained 默认声明的对象是 strong 类型的,赋值时有可能会产生 retain/release 调用,如果一个变量在其生命周期内不会被释放,则使用 unsafe_unretained 会节省很大的开销,可以理解为Weak,不参与retain和release的操作,不属于编译器的内存管理对象 这里有个比较好的解释

知识点10:查表 当遇到多项选择的条件时,要尽量使用查表法实现,比如 switch/case

知识点11:JSON和Model转换前,个数都是已知的,在ModelSetWithDictionaryFunction最终转换函数中,如果属性多,那么以Json个数为主,如果Json多,那么判断while (propertyMeta) 是否进行类型转换,以个数少的为基准遍历

YYModel中的缓存,以及各种非常细节的性能优化相关(例如6,7,8,9,10,11),因此这个Demo里面的各种性能对比也是非常优秀的,非常细节,思路非常清晰,既然用了,了解看下源码,受益匪浅

大神博客

iOS高性能Model转换框架----YYModel学习相关推荐

  1. iOS开发--一些开源的学习资源

    vim插件: https://github.com/Valloric/YouCompleteMe vim插件配置: https://github.com/spf13/spf13-vim ------- ...

  2. 高性能Cordova App开发学习笔记

    高性能Cordova App开发学习笔记 文件结构 添加插件 构建准备 各个www的作用,prepare命令会将hello\www的内容会拷贝到platform下的wwww目录,知道该改哪里了吧?如果 ...

  3. 《Linux高性能服务器编程》学习笔记

    <Linux高性能服务器编程>学习笔记 Linux高性能服务器编程 TCP/IP协议族 TCP/IP协议族体系结构以及主要协议 数据链路层 网络层 传输层 应用层 封装 分用 测试网络 A ...

  4. 《深度探索C++对象模型(Inside The C++ Object Model )》学习笔记

    来源:http://dsqiu.iteye.com/blog/1669614 之前一直对C++内部的原理的完全空白,然后找到<Inside The C++ Object Model>这本书 ...

  5. 视频教程-【CVPR2018】3D Pose Estimation and 3D Model Retriev-强化学习

    [CVPR2018]3D Pose Estimation and 3D Model Retriev 从事IT领域8年,精通计算机视觉. 吴勤明 ¥50.00 立即订阅 扫码下载「CSDN程序员学院AP ...

  6. 用Python对CAPM和Fama French Three Factor model的初步学习

    用Python对CAPM和Fama French Three Factor model的初步学习 概述 介绍 建模与分析 CAPM Fama French Three-Factor Model 总结 ...

  7. iOS工程师2021年应该学习一下Vapor

    iOS工程师2021年应该学习一下Vapor Swift席卷了编程领域.它是目前发展最快的语言之一.云计算每天都在改变世界,现在您可以使用Swift语言的强大功能和简单性来创建支持云的应用程序. Va ...

  8. iOS开发60分钟入门学习精华

    有过脚本开发经验的人(如Javascript,PHP,Shell)在刚开始学习iOS开发的时候,会觉得iOS开发的学习曲线比脚本语言要高,是的,这种感觉是对的.因为学iOS开发,不仅是学习一门新语言, ...

  9. iOS 高性能置灰方案

    iOS 高性能置灰方案 全局动态置灰,很多App都会在特殊节日比如清明或者其他哀悼日使用哀悼置灰. 对于置灰操作,如果是每次都更新版本,肯定就来不及,一般处理方式为后台设置按钮开关,处理对应日子开启置 ...

最新文章

  1. 剑指offer:面试题14- II. 剪绳子 II
  2. 【Python】卸载完Python3 之后 Python2 无法打开IDLE
  3. 万物之始正则表达式全解析三部曲(中篇)-正则表达式运算符优先级及匹配规则
  4. Hyperledger Fabric 链码(3) 生命周期和API
  5. linux rmp命令安装包在哪里_【MongoDB系列】Linux系统安装MongoDB
  6. 使用Exchange 2007的几个注意事项
  7. C++ primer 第13章 拷贝控制
  8. 《程序员面试宝典》笔记一
  9. window服务程序编写及发布
  10. c语言怎么下载步骤,C语言教程下载_C语言教程APP手机最新版安装 - 风云下载
  11. 企业办理CMMI认证是怎么收费的?
  12. Oracle数据库经典50题(附答案),写完不理解的来打我
  13. Vue抽离公共方法并全局注册使用
  14. TAP-Win32 Adapter OAS“的网络适配器删除后总是出现(kms激活器及win10激活)
  15. wps教鞭功能_你未必知道的WPS神奇功能
  16. 一、响应式编程基本介绍
  17. 三星高价卖苹果iPhone X OLED屏幕, 苹果或寻新队友
  18. Basemap绘制地图
  19. 图卷积神经网络(GCN)综述与实现(PyTorch版)
  20. 杨白劳 or 黄世仁

热门文章

  1. python requests 乱码
  2. 2020 年我学习到的 20 条软件工程准则
  3. 买华为手机U8825D的体验
  4. 计算机组成原理实验:全加器实验
  5. [Computer Architecture读书笔记] H.2 Detecting and Enhancing Loop-Level Parallelism
  6. maven项目查看依赖树
  7. 初学C语言:判断输入的数是否能被5整除。
  8. bash shell 命令 - linux
  9. 码农场 » POJ 2566 Bound Found 题解 《挑战程序设计竞赛》
  10. 登录mysql时遇到了:mysql: [Warning] Using a password on the command line interface can be insecure.