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变量处:

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_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方法:

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 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:

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指针来说,网上有一个特别多转载的图:
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) {                                            //如果没有缓存,则第一次解析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:

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

0 0
原创粉丝点击