YYModel源代码分析(二)YYClassInfo
来源:互联网 发布:linux shell写while 编辑:程序博客网 时间:2024/06/06 00:47
前言
本文的中文注释代码demo更新在我的github上。
上篇 YYModel源代码分析(一)整体介绍
主要写了YYModel的整体结构,代码调用思路以及头文件YYModel.h代码。本篇会主要集中在YYClassInfo文件上。文章内容会包含一些与JSONModel的比较,想了解JSONModel,可以参考JSONModel源代码解析。
主体分层
YYClassInfo主要分为以下几部分:
typedef NS_OPTIONS(NSUInteger, YYEncodingType)
与YYEncodingType YYEncodingGetType(const char *typeEncoding);
方法@interface YYClassIvarInfo : NSObject
@interface YYClassMethodInfo : NSObject
@interface YYClassPropertyInfo : NSObject
@interface YYClassInfo : NSObject
以下将分别分析每一部分的源代码。
YYClassInfo源代码
(1).typedef NS_OPTIONS(NSUInteger, YYEncodingType)
与YYEncodingType YYEncodingGetType(const char *typeEncoding);
方法
相关知识
在这边,对于YYEncodingType的了解,需要知道一个很重要的概念:
Type Encodings
To assist the runtime system, the compiler encodes the return and argument types for each method in a character string and associates the string with the method selector. The coding scheme it uses is also useful in other contexts and so is made publicly available with the@encode()
compiler directive. When given a type specification, @encode()
returns a string encoding that type. The type can be a basic type such as an int
, a pointer, a tagged structure or union, or a class name—any type, in fact, that can be used as an argument to the Csizeof()
operator.
看过我JSONModel解析的人,应该比较了解这一块,property attribute的解析就是通过type encode解析出来的string进行的解析。
但在这里的type encoding相对于JSONModel中的使用,会更general一些:
* JSONModel是只针对于Class的property变量,所以在解析的时候,将Ivar默认包含在property中,通过property的property attribute一并解析出来。
* YYModel中,包含了Class的property变量,还加上了Class的Method方法与Ivar的实例变量。对于Ivar来说,可能还存在在方法参数中,所以说所需要解析的类型会更加多一些。
代码
在YYClassInfo.h中,先定义了一个NS_OPTIONS:
typedef NS_OPTIONS(NSUInteger, YYEncodingType) { //0~8位:变量类型 YYEncodingTypeMask = 0xFF, ///< mask of type value YYEncodingTypeUnknown = 0, ///< unknown YYEncodingTypeVoid = 1, ///< void YYEncodingTypeBool = 2, ///< bool YYEncodingTypeInt8 = 3, ///< char / BOOL YYEncodingTypeUInt8 = 4, ///< unsigned char YYEncodingTypeInt16 = 5, ///< short YYEncodingTypeUInt16 = 6, ///< unsigned short YYEncodingTypeInt32 = 7, ///< int YYEncodingTypeUInt32 = 8, ///< unsigned int YYEncodingTypeInt64 = 9, ///< long long YYEncodingTypeUInt64 = 10, ///< unsigned long long YYEncodingTypeFloat = 11, ///< float YYEncodingTypeDouble = 12, ///< double YYEncodingTypeLongDouble = 13, ///< long double YYEncodingTypeObject = 14, ///< id YYEncodingTypeClass = 15, ///< Class YYEncodingTypeSEL = 16, ///< SEL YYEncodingTypeBlock = 17, ///< block YYEncodingTypePointer = 18, ///< void* YYEncodingTypeStruct = 19, ///< struct YYEncodingTypeUnion = 20, ///< union YYEncodingTypeCString = 21, ///< char* YYEncodingTypeCArray = 22, ///< char[10] (for example) //8~16位:方法类型 YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier YYEncodingTypeQualifierConst = 1 << 8, ///< const YYEncodingTypeQualifierIn = 1 << 9, ///< in YYEncodingTypeQualifierInout = 1 << 10, ///< inout YYEncodingTypeQualifierOut = 1 << 11, ///< out YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy YYEncodingTypeQualifierByref = 1 << 13, ///< byref YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway //16~24位:property修饰类型 YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly YYEncodingTypePropertyCopy = 1 << 17, ///< copy YYEncodingTypePropertyRetain = 1 << 18, ///< retain YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic YYEncodingTypePropertyWeak = 1 << 20, ///< weak YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter= YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter= YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic};
该NS_OPTIONS主要定义了3个大类encode type:
* YYEncodingTypeMask:
变量类型,因为类型只会有一种,所以就用数字站位
* YYEncodingTypeQualifierMask:
方法中的参数变量修饰符,理论上只有解析Method的参数才能解析到
* YYEncodingTypePropertyMask
property修饰符类型
这边对于YYEncodingTypeQualifierMask和YYEncodingTypePropertyMask因为存在多种可能的情况,使用了位移(<<)的方式,通过与(&)YYEncodingTypeQualifierMask和YYEncodingTypePropertyMask的方式,判断是否包含某个值。
获取Ivar类型的函数如下:
//解析Ivar的type encode stringYYEncodingType YYEncodingGetType(const char *typeEncoding) { char *type = (char *)typeEncoding; if (!type) return YYEncodingTypeUnknown; size_t len = strlen(type); if (len == 0) return YYEncodingTypeUnknown; YYEncodingType qualifier = 0; bool prefix = true; while (prefix) { //方法参数Ivar中的解析,理论上解析不到该类参数 switch (*type) { case 'r': { qualifier |= YYEncodingTypeQualifierConst; type++; } break; case 'n': { qualifier |= YYEncodingTypeQualifierIn; type++; } break; case 'N': { qualifier |= YYEncodingTypeQualifierInout; type++; } break; case 'o': { qualifier |= YYEncodingTypeQualifierOut; type++; } break; case 'O': { qualifier |= YYEncodingTypeQualifierBycopy; type++; } break; case 'R': { qualifier |= YYEncodingTypeQualifierByref; type++; } break; case 'V': { qualifier |= YYEncodingTypeQualifierOneway; type++; } break; default: { prefix = false; } break; } } len = strlen(type); if (len == 0) return YYEncodingTypeUnknown | qualifier; //返回值类型解析 switch (*type) { case 'v': return YYEncodingTypeVoid | qualifier; case 'B': return YYEncodingTypeBool | qualifier; case 'c': return YYEncodingTypeInt8 | qualifier; case 'C': return YYEncodingTypeUInt8 | qualifier; case 's': return YYEncodingTypeInt16 | qualifier; case 'S': return YYEncodingTypeUInt16 | qualifier; case 'i': return YYEncodingTypeInt32 | qualifier; case 'I': return YYEncodingTypeUInt32 | qualifier; case 'l': return YYEncodingTypeInt32 | qualifier; case 'L': return YYEncodingTypeUInt32 | qualifier; case 'q': return YYEncodingTypeInt64 | qualifier; case 'Q': return YYEncodingTypeUInt64 | qualifier; case 'f': return YYEncodingTypeFloat | qualifier; case 'd': return YYEncodingTypeDouble | qualifier; case 'D': return YYEncodingTypeLongDouble | qualifier; case '#': return YYEncodingTypeClass | qualifier; case ':': return YYEncodingTypeSEL | qualifier; case '*': return YYEncodingTypeCString | qualifier; case '^': return YYEncodingTypePointer | qualifier; case '[': return YYEncodingTypeCArray | qualifier; case '(': return YYEncodingTypeUnion | qualifier; case '{': return YYEncodingTypeStruct | qualifier; case '@': { if (len == 2 && *(type + 1) == '?') return YYEncodingTypeBlock | qualifier; //OC Block else return YYEncodingTypeObject | qualifier; //OC对象 } default: return YYEncodingTypeUnknown | qualifier; }}
该函数也是通过获得的type encode的string,对照着表进行解析,因为是解析Ivar,所以也只包含了YYEncodingTypeMask和YYEncodingTypeQualifierMask。而YYEncodingTypePropertyMask会包含在property的解析中。
(2).@interface YYClassIvarInfo : NSObject
相关知识
我下载了runtime的源代码objc4-680.tar.gz,以下代码基于该版本。该版本包含旧代码与新代码,以下全部基于新代码。
**Ivar:**An opaque type that represents an instance variable(实例变量,跟某个对象关联,不能被静态方法使用,与之想对应的是class variable).
typedef struct ivar_t *Ivar;struct ivar_t {#if __x86_64__ // *offset was originally 64-bit on some x86_64 platforms. // We read and write only 32 bits of it. // Some metadata provides all 64 bits. This is harmless for unsigned // little-endian values. // Some code uses all 64 bits. class_addIvar() over-allocates the // offset for their benefit.#endif int32_t *offset; const char *name; const char *type; // alignment is sometimes -1; use alignment() instead uint32_t alignment_raw; uint32_t size; uint32_t alignment() const { if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT; return 1 << alignment_raw; }};
代码
YYClassIvarInfo类声明:
/** Instance variable information. */@interface YYClassIvarInfo : NSObject@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct ivar本身指针@property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name ivar名@property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset ivar偏移量@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding ivar encode string@property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type ivar encode解析值/** Creates and returns an ivar info object. @param ivar ivar opaque struct @return A new object, or nil if an error occurs. */- (instancetype)initWithIvar:(Ivar)ivar;@end
initWithIvar
方法实现:
- (instancetype)initWithIvar:(Ivar)ivar { if (!ivar) return nil; self = [super init]; _ivar = ivar; const char *name = ivar_getName(ivar); //获取ivar名 if (name) { _name = [NSString stringWithUTF8String:name]; } _offset = ivar_getOffset(ivar); //获取便宜量 const char *typeEncoding = ivar_getTypeEncoding(ivar); //获取类型encode string if (typeEncoding) { _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; _type = YYEncodingGetType(typeEncoding); //类型解析 } return self;}
YYClassIvarInfo本身就是对系统Ivar的一层封装,并进行了一次类型的解析。
实例
用YYModel测试用例来进行观察:
YYTestNestRepo实现:
@interface YYTestNestRepo : NSObject@property uint64_t repoID;@property NSString *name;@property YYTestNestUser *user;@end@implementation YYTestNestRepo@end
YYTestNestRepo调用:
NSString *json = @"{\"repoID\":1234,\"name\":\"YYModel\",\"user\":{\"uid\":5678,\"name\":\"ibireme\"}}";YYTestNestRepo *repo = [YYTestNestRepo yy_modelWithJSON:json];
设置解析断点在解析@property YYTestNestUser *user;
的Ivar变量处:
(3).@interface YYClassMethodInfo : NSObject
相关知识
**Method:**An opaque type that represents a method in a class definition.
typedef struct method_t *Method;struct method_t { SEL name; const char *types; IMP imp; struct SortBySELAddress : public std::binary_function<const method_t&, const method_t&, bool> { bool operator() (const method_t& lhs, const method_t& rhs) { return lhs.name < rhs.name; } };};
其中包含两个结构体SEL和IMP:
**SEL:**An opaque type that represents a method selector
Method selectors are used to represent the name of a method at runtime. A method selector is a C string that has been registered (or “mapped“) with the Objective-C runtime. Selectors generated by the compiler are automatically mapped by the runtime when the class is loaded.
typedef struct objc_selector *SEL;
**IMP:**A pointer to the function of a method implementation
This data type is a pointer to the start of the function that implements the method. This function uses standard C calling conventions as implemented for the current CPU architecture. The first argument is a pointer to self (that is, the memory for the particular instance of this class, or, for a class method, a pointer to the metaclass). The second argument is the method selector. The method arguments follow.
#if !OBJC_OLD_DISPATCH_PROTOTYPEStypedef void (*IMP)(void /* id, SEL, ... */ ); #elsetypedef id (*IMP)(id, SEL, ...); #endif
代码
YYClassMethodInfo类声明:
@interface YYClassMethodInfo : NSObject@property (nonatomic, assign, readonly) Method method; ///< method opaque struct method指针@property (nonatomic, strong, readonly) NSString *name; ///< method name method名@property (nonatomic, assign, readonly) SEL sel; ///< method's selector method selector@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation method implementation@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types method的参数和返回类型@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type method返回值的encode types@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type method参数列表- (instancetype)initWithMethod:(Method)method;@end
initWithMethod
方法实现:
- (instancetype)initWithMethod:(Method)method { if (!method) return nil; self = [super init]; _method = method; _sel = method_getName(method); //获取方法名,在oc中,方法名就是selector的标志 _imp = method_getImplementation(method); //获取方法实现 const char *name = sel_getName(_sel); if (name) { _name = [NSString stringWithUTF8String:name]; } const char *typeEncoding = method_getTypeEncoding(method); //获得方法参数和返回值 if (typeEncoding) { _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; } char *returnType = method_copyReturnType(method); //获得返回值encode string if (returnType) { _returnTypeEncoding = [NSString stringWithUTF8String:returnType]; free(returnType); } unsigned int argumentCount = method_getNumberOfArguments(method); //获得方法参数数量 if (argumentCount > 0) { NSMutableArray *argumentTypes = [NSMutableArray new]; for (unsigned int i = 0; i < argumentCount; i++) { //遍历参数 char *argumentType = method_copyArgumentType(method, i); //获得该参数的encode string NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil; [argumentTypes addObject:type ? type : @""]; if (argumentType) free(argumentType); } _argumentTypeEncodings = argumentTypes; } return self;}
实例
用YYModel测试用例来进行观察:
YYTestNestRepo实现:
@interface YYTestNestRepo : NSObject@property uint64_t repoID;@property NSString *name;@property YYTestNestUser *user;@end@implementation YYTestNestRepo@end
YYTestNestRepo调用:
NSString *json = @"{\"repoID\":1234,\"name\":\"YYModel\",\"user\":{\"uid\":5678,\"name\":\"ibireme\"}}";YYTestNestRepo *repo = [YYTestNestRepo yy_modelWithJSON:json];
设置解析断点解析user
方法:
对于property来说,本质是:Ivar+getter+setter,所以设置了property也会触发initWithMethod解析-(YYTestNestUser *) user;
方法,该方法的解析如上图。
这边比较有意思的是,明明user没有参数,怎么method_getNumberOfArguments
解析出来2个参数
原因就是方法调用最后都会转成((void (*)(id, SEL))objc_msgSend)((id)m, @selector(user));
,所以会有两个参数。
(4).@interface YYClassPropertyInfo : NSObject
相关知识
**Property:**An opaque type that represents an Objective-C declared property.
typedef struct property_t *objc_property_t;struct property_t { const char *name; const char *attributes;};
其中对于attributes就是property属性的encode string。具体解析可以参考JSONModel的文章。
代码
YYClassPropertyInfo类声明:
@interface YYClassPropertyInfo : NSObject@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct property指针@property (nonatomic, strong, readonly) NSString *name; ///< property's name property名@property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type property encode解析值@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value property encode string@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name property对应的ivar名字@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil property如果是oc类型,oc类型对应的class@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil property如果存在protocol,protocol列表@property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull) property的getter方法@property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull) property的setter方法- (instancetype)initWithProperty:(objc_property_t)property;@end
initWithProperty
方法实现:
- (instancetype)initWithProperty:(objc_property_t)property { if (!property) return nil; self = [super init]; _property = property; const char *name = property_getName(property); //获得property名 if (name) { _name = [NSString stringWithUTF8String:name]; } YYEncodingType type = 0; unsigned int attrCount; objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); //获得所有property的attribute array for (unsigned int i = 0; i < attrCount; i++) { switch (attrs[i].name[0]) { case 'T': { // Type encoding 表示是property类型 if (attrs[i].value) { _typeEncoding = [NSString stringWithUTF8String:attrs[i].value]; //获得attribute的encode string type = YYEncodingGetType(attrs[i].value); //解析type if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) { //代表是OC类型 NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding]; //扫描attribute的encode string if (![scanner scanString:@"@\"" intoString:NULL]) continue; //不包含@\"代表不是oc类型,跳过 NSString *clsName = nil; if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) { //扫描oc类型string,在 \"之前 if (clsName.length) _cls = objc_getClass(clsName.UTF8String); //获得oc对象类型,并附值 } NSMutableArray *protocols = nil; while ([scanner scanString:@"<" intoString:NULL]) { //扫描<>中的protocol类型,并设置 NSString* protocol = nil; if ([scanner scanUpToString:@">" intoString: &protocol]) { if (protocol.length) { if (!protocols) protocols = [NSMutableArray new]; [protocols addObject:protocol]; } } [scanner scanString:@">" intoString:NULL]; } _protocols = protocols; } } } break; case 'V': { // Instance variable //ivar变量 if (attrs[i].value) { _ivarName = [NSString stringWithUTF8String:attrs[i].value]; } } break; case 'R': { //以下为property的几种类型扫描,setter和getter方法要记录方法名 type |= YYEncodingTypePropertyReadonly; } break; case 'C': { type |= YYEncodingTypePropertyCopy; } break; case '&': { type |= YYEncodingTypePropertyRetain; } break; case 'N': { type |= YYEncodingTypePropertyNonatomic; } break; case 'D': { type |= YYEncodingTypePropertyDynamic; } break; case 'W': { type |= YYEncodingTypePropertyWeak; } break; case 'G': { type |= YYEncodingTypePropertyCustomGetter; if (attrs[i].value) { _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); } } break; case 'S': { type |= YYEncodingTypePropertyCustomSetter; if (attrs[i].value) { _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); } } // break; commented for code coverage in next line default: break; } } if (attrs) { //有attrs要free free(attrs); attrs = NULL; } _type = type; //最后设置encode解析值 if (_name.length) { //设置默认的getter方法和setter方法 if (!_getter) { _getter = NSSelectorFromString(_name); } if (!_setter) { _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]); } } return self;}
这段的解析方式和之前JSONModel的解析property方式有些类似,也不多做介绍了。
实例
用YYModel测试用例来进行观察:
YYTestNestRepo实现:
@interface YYTestNestRepo : NSObject@property uint64_t repoID;@property NSString *name;@property YYTestNestUser *user;@end@implementation YYTestNestRepo@end
YYTestNestRepo调用:
NSString *json = @"{\"repoID\":1234,\"name\":\"YYModel\",\"user\":{\"uid\":5678,\"name\":\"ibireme\"}}";YYTestNestRepo *repo = [YYTestNestRepo yy_modelWithJSON:json];
设置解析断点解析property-user:
(5).@interface YYClassInfo : NSObject
相关知识
**Class:**An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;struct objc_class : objc_object { Class superclass; const char *name; uint32_t version; uint32_t info; uint32_t instance_size; struct old_ivar_list *ivars; struct old_method_list **methodLists; Cache cache; struct old_protocol_list *protocols; // CLS_EXT only const uint8_t *ivar_layout; struct old_class_ext *ext; ...}struct objc_object {private: isa_t isa;public: ...}
对Class的superclass和isa指针来说,网上有一个特别多转载的图:
上图实线是 super_class 指针,虚线是isa指针。 有趣的是根元类的超类是NSObject,而isa指向了自己,而NSObject的超类为nil,也就是它没有超类。
代码
YYClassInfo类声明:
@interface YYClassInfo : NSObject@property (nonatomic, assign, readonly) Class cls; ///< class object class指针@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object superClass指针@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object metaClass指针@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class 是否该class是metaclass@property (nonatomic, strong, readonly) NSString *name; ///< class name class名@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info superClass的classinfo(缓存)@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars ivar的dictionary,key为ivar的name@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods method的dictionary,key为method的name@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties properties的dictionary,key为property的name//设置class更新,比如动态增加了一个方法,需要更新class- (void)setNeedUpdate;//返回class是否需要更新,更新则应该调用下面两个方法之一- (BOOL)needUpdate;+ (nullable instancetype)classInfoWithClass:(Class)cls;+ (nullable instancetype)classInfoWithClassName:(NSString *)className;@end
YYClassInfo中有一个needUpdate是否更新的标识符,当手动更改class结构(比如class_addMethod()
等)的时候,可以调用方法:
@implementation YYClassInfo { BOOL _needUpdate; //是否需要更新private变量}- (void)setNeedUpdate { //设置需要更新 _needUpdate = YES;}- (BOOL)needUpdate { //返回是否需要更新 return _needUpdate;}
实际的解析方法:
//多一层class从nsstring到class对象的转换+ (instancetype)classInfoWithClassName:(NSString *)className { Class cls = NSClassFromString(className); return [self classInfoWithClass:cls];}//class解析主体方法+ (instancetype)classInfoWithClass:(Class)cls { if (!cls) return nil; static CFMutableDictionaryRef classCache; //class缓存 static CFMutableDictionaryRef metaCache; //meta class缓存 static dispatch_once_t onceToken; static dispatch_semaphore_t lock; //锁 dispatch_once(&onceToken, ^{ //初始化两种缓存 classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); lock = dispatch_semaphore_create(1); }); dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); //只允许同时1个线程 YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); //获取曾经解析过的缓存 if (info && info->_needUpdate) { //如果存在且需要更新,则重新解析class并更新结构体 [info _update]; } dispatch_semaphore_signal(lock); //释放锁 if (!info) { //如果没有缓存,则第一次解析class info = [[YYClassInfo alloc] initWithClass:cls]; if (info) { //解析完毕设置缓存 dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); dispatch_semaphore_signal(lock); } } return info;}
classInfoWithClass
方法中主要调用了两个方法- (instancetype)initWithClass:(Class)cls
(初始化class)和- (void)_update
(更新class),接下来看该两个方法的实现。
//初始化class对象方法- (instancetype)initWithClass:(Class)cls { if (!cls) return nil; self = [super init]; _cls = cls; _superCls = class_getSuperclass(cls); //设置superclass _isMeta = class_isMetaClass(cls); //判断是否是metaclass if (!_isMeta) { //不是的话获得meta class _metaCls = objc_getMetaClass(class_getName(cls)); } _name = NSStringFromClass(cls); //获得类名 [self _update]; //进行更新 _superClassInfo = [self.class classInfoWithClass:_superCls]; //递归superclass return self;}
这边也用到了``- (void)_update
(更新class),这应该就是class的核心更新方法:
//更新函数- (void)_update { _ivarInfos = nil; //重置ivar,mthod,property3个缓存dictionary _methodInfos = nil; _propertyInfos = nil; Class cls = self.cls; unsigned int methodCount = 0; Method *methods = class_copyMethodList(cls, &methodCount); if (methods) { //解析method,并以name为key,进行缓存设置 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) { //解析property,并以name为key,进行缓存设置 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) { //解析ivar,并以name为key,进行缓存设置 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 = @{}; //如果不存在相应的方法,则初始化空的dictionary给相应的方法 if (!_methodInfos) _methodInfos = @{}; if (!_propertyInfos) _propertyInfos = @{}; _needUpdate = NO; //已经更新完成,设no}
该函数虽然比较长,但也比较好理解,就是将method,property,ivar全部取出并附值给缓存。
实例
用YYModel测试用例来进行观察:
YYTestNestRepo实现:
@interface YYTestNestRepo : NSObject@property uint64_t repoID;@property NSString *name;@property YYTestNestUser *user;@end@implementation YYTestNestRepo@end
YYTestNestRepo调用:
NSString *json = @"{\"repoID\":1234,\"name\":\"YYModel\",\"user\":{\"uid\":5678,\"name\":\"ibireme\"}}";YYTestNestRepo *repo = [YYTestNestRepo yy_modelWithJSON:json];
设置解析断点解析class YYTestNestRepo:
至此,整个model的class信息全部被解析完成,然后设置到了YYClassInfo类型的上。
小结
相对于JSONModel只对Property进行解析然后缓存。
YYModel将Class的Method,Property,Ivar全部进行了解析与缓存。
其中比较亮点的地方:
* 1.Ivar和property解析出来的YYEncodingType
* 2.CFMutableDictionaryRef的缓存
* 3.可以动态更新的needUpdate
参考资料
简书地址
1.郑钦洪_:YYModel 源码历险记
2.Objective-C Runtime Programming Guide - Declared Properties
3.Objective-C Runtime Programming Guide - Type Encodings
4.runtime源代码
5.Objective-C Runtime的数据类型
6.轻松学习之三——IMP指针的作用
7.runtime Method
8.apple-objective_c runtime
9.Objective-C Runtime
- YYModel源代码分析(二)YYClassInfo
- YYModel源代码分析(三)NSObject+YYModel
- iOS源码解析—YYModel(YYClassInfo)
- YYModel源代码分析(一)整体介绍
- TCPMP源代码分析(二)
- Iperf 源代码分析(二)
- Hadoop源代码分析(二)
- yaffs2源代码分析(二)
- Iperf 源代码分析(二)
- yaffs2源代码分析(二)
- Gzip源代码分析(二)
- Iperf 源代码分析(二)
- Linux MTD源代码分析(二)
- Chrome源代码分析之socket(二)
- WebKit内核源代码分析(二)
- WebKit内核源代码分析(二)
- WebKit内核源代码分析(二)
- WebKit内核源代码分析(二)
- SAP的启动、停止
- 二叉树的创建与遍历
- 建议25:谨慎集合属性的可写操作
- map
- 说说Android应用的persistent属性
- YYModel源代码分析(二)YYClassInfo
- 关于jQuery分页
- 服务器重启后,启动SAP ERP的步骤
- 自定义 Dialog方式的加载动画
- Java增加窗口
- 详解当当网的分布式架构Elastic-Job
- 字符编码问题
- RabbitMQ指南
- SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)