YYModel解析2
主体分层
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 valueYYEncodingTypeUnknown = 0, ///< unknownYYEncodingTypeVoid = 1, ///< voidYYEncodingTypeBool = 2, ///< boolYYEncodingTypeInt8 = 3, ///< char / BOOLYYEncodingTypeUInt8 = 4, ///< unsigned charYYEncodingTypeInt16 = 5, ///< shortYYEncodingTypeUInt16 = 6, ///< unsigned shortYYEncodingTypeInt32 = 7, ///< intYYEncodingTypeUInt32 = 8, ///< unsigned intYYEncodingTypeInt64 = 9, ///< long longYYEncodingTypeUInt64 = 10, ///< unsigned long longYYEncodingTypeFloat = 11, ///< floatYYEncodingTypeDouble = 12, ///< doubleYYEncodingTypeLongDouble = 13, ///< long doubleYYEncodingTypeObject = 14, ///< idYYEncodingTypeClass = 15, ///< ClassYYEncodingTypeSEL = 16, ///< SELYYEncodingTypeBlock = 17, ///< blockYYEncodingTypePointer = 18, ///< void*YYEncodingTypeStruct = 19, ///< structYYEncodingTypeUnion = 20, ///< unionYYEncodingTypeCString = 21, ///< char*YYEncodingTypeCArray = 22, ///< char[10] (for example)//8~16位:方法类型YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifierYYEncodingTypeQualifierConst = 1 << 8, ///< constYYEncodingTypeQualifierIn = 1 << 9, ///< inYYEncodingTypeQualifierInout = 1 << 10, ///< inoutYYEncodingTypeQualifierOut = 1 << 11, ///< outYYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopyYYEncodingTypeQualifierByref = 1 << 13, ///< byrefYYEncodingTypeQualifierOneway = 1 << 14, ///< oneway//16~24位:property修饰类型YYEncodingTypePropertyMask = 0xFF0000, ///< mask of propertyYYEncodingTypePropertyReadonly = 1 << 16, ///< readonlyYYEncodingTypePropertyCopy = 1 << 17, ///< copyYYEncodingTypePropertyRetain = 1 << 18, ///< retainYYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomicYYEncodingTypePropertyWeak = 1 << 20, ///< weakYYEncodingTypePropertyCustomGetter = 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 string
YYEncodingType 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 Blockelsereturn 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.
#endifint32_t *offset;const char *name;const char *type;// alignment is sometimes -1; use alignment() insteaduint32_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 stringif (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变量处:
YYClassIvarInfo-user
(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_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef 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 stringif (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 stringNSString *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
方法:
YYClassMethodInfo-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 arrayfor (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 stringtype = YYEncodingGetType(attrs[i].value); //解析typeif ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) { //代表是OC类型NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding]; //扫描attribute的encode stringif (![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 linedefault: break;}}if (attrs) { //有attrs要freefree(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:
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 onlyconst uint8_t *ivar_layout;struct old_class_ext *ext;...
}struct objc_object {
private:isa_t isa;public:...
}
对Class的superclass和isa指针来说,网上有一个特别多转载的图:
superclass and metaclass
上图实线是 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) { //如果没有缓存,则第一次解析classinfo = [[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); //判断是否是metaclassif (!_isMeta) { //不是的话获得meta class_metaCls = objc_getMetaClass(class_getName(cls));}_name = NSStringFromClass(cls); //获得类名[self _update]; //进行更新_superClassInfo = [self.class classInfoWithClass:_superCls]; //递归superclassreturn 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:
class YYTestNestRepo
至此,整个model的class信息全部被解析完成,然后设置到了YYClassInfo类型的上。
小结
相对于JSONModel只对Property进行解析然后缓存。
YYModel将Class的Method,Property,Ivar全部进行了解析与缓存。
其中比较亮点的地方:
- 1.Ivar和property解析出来的YYEncodingType
- 2.CFMutableDictionaryRef的缓存
- 3.可以动态更新的needUpdate
YYModel解析2相关推荐
- swift使用yymodel解析数组模型
这里写目录标题 桥接文件 模型类 解析json的代码 上一篇我已经写了,用swift建立模型类,然后在oc文件中,使用yymodel来解析模型,这次,写一个纯swift使用yy_model来解析数组j ...
- swift创建嵌套模型使用yy_model解析json
依然是使用swift混编,因为现在的项目是oc项目,但是不想写太多oc代码,所以用swift创建模型.因为swift创建模型可以很方便的输入默认值代码量也少,这样在读取json的时候就不用担心,因为j ...
- YYModel使用详解
根据需求.后台返回如下数据.需要解析为model类.方便操作数据. NSDictionary *returnDic = @{@"birthPlace":@"宁夏固原市彭阳 ...
- 一个iOS流畅性优化工具
????????关注后回复 "进群" ,拉你进程序员交流群???????? 转自:掘金 BangRaJun https://juejin.cn/post/693472015254 ...
- ios开发循环网络请求_GitHub - JadenTeng/ResourceX: iOS网络请求,网络泛型编程,工具类的封装,基于AFNetworking 实现, NSCache数据缓存...
ResourceX 通过AFNetworking.YYModel 解析网络泛型编程简化网络请求 现如今,网络通信几乎涉及每一个app程序.对于绝大多数请求HTTP API的方法,它们的执行流程都可以分 ...
- YYModel 简单解析
YYModel现在项目用来自动解析json数据转model,简单记录下原理. 第一步是收集目标类的类名.元类.ivarlist.methodlist.propertylist.再递归的收集 - (in ...
- YYModel底层解析- Runtime
这段时间一直在忙新的需求,没有时间来整理代码,发表自己技术博客,今天我们来看一下YYModel的底层解析以及如何使用,希望对大家有所帮助! 一 概述 概括 YYModel是一个轻量级的JSON模型转换 ...
- YYModel V1.0.4源码解析
YYKit出现了很长时间了,一直想要详细解析一下它的源码,都是各种缘由推迟了. 最近稍微闲了一点,决定先从最简单的YYModel开始吧. 首先,我也先去搜索了一下YYModel相关的文章,解析主要AP ...
- iOS之深入解析YYModel的底层原理
一.前言 YYModel 是由 ibireme 开发的一套小而精美的模型转换框架,采用分类的形式,无需继承框架的某个基类就可以方便地完成模型的转换,且内部做了自动类型转换和安全处理,可以有效地防止因模 ...
最新文章
- 【翻译】How-To: Using the N* Stack, part 3
- (转载)VS2010/MFC编程入门之四(MFC应用程序框架分析)
- 三层交换机有什么优势?
- 论文浅尝 | 从 6 篇顶会论文看「知识图谱」领域最新研究进展 | 解读 代码
- mac 下 ~/.bash_profile无效
- 如何商业智能平台BI的成本
- 在自定义HttpHandler 中使用Session
- NLP(自然语言处理技术)
- 计算机的硬件软件组成
- 用matlab音频欠采样率时域采样,matlab时域采样定理.doc
- Windows10虚拟机安转(详细版)
- vim 配置(ma6174 + YCM)
- CHM文件制作方法及制作中遇到的坑
- 速看——揭秘“微商”的盈利模式
- Nuki智能锁安全性分析
- oracle 倒库详细步骤,科二倒车入库操作步骤高清图解,一步一解读,非常实用!...
- git push时报错error: File: xxx 252.15 MB, exceeds 100.00 MB.
- matlab wash矩阵产生,洗衣机净衣效能与衣损程度的关系分析
- 戴尔win10开机后,在桌面点右键一直转圈,但任务栏又能正常点击
- 圆锥的表面积和体积计算