YYClassInfo源码解析

1,427 阅读13分钟

概况

YYClassInfo作为YYModel的核心类,是YYModel解析Json转模型必不可少的部分,其实它主要是利用runTime来获取class内部信息,从而为json转model提供了便利工具。学习YYClassInfo类有助于理解YYModel的工作原理,也有利于加强runTime的基本功。Let's get started.

YYClassInfo类预览

YYClassInfo类.h文件和.m文件其实都是可以分为5个部分,(读者可以看下源代码对比下)分别为:

  1. YYEncodingType
  2. YYClassIvarInfo
  3. YYClassMethodInfo
  4. YYClassPropertyInfo
  5. YYClassInfo
    每个部分负责一个具体的职责,根据5部分顺序,接下来进行逐一的具体分析。

YYEncodingType模块

//YYClassInfo.h
/**
 Type encoding's type.
 */
typedef NS_OPTIONS(NSUInteger, YYEncodingType) {
    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)
    
    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
    
    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
};

YYEncodingType YYEncodingGetType(const char *typeEncoding);

解析:以上是YYClassInfo.h文件关于YYEncodingType模块的源代码。首先定义了一个移位枚举类型,该类型的枚举值可以按位取或操作实现多种类型的组合。
其中YYEncodingTypexxx记录了属性的类型,YYEncodingTypeQualifier记录了属性的关键字,YYEncodingTypeProperty记录了属性的修饰。其中YYEncodingTypeMask,YYEncodingTypeQualifierMask,YYEncodingTypePropertyMask,可以实现取值操作,比如其中YYEncodingTypeMask去和枚举值取&操作可以得到低8位的值也就是该枚举的值。也就是说多个枚举值取|操作可以实现多个枚举值的组合,如一个变量可以存储三个枚举值(YYEncodingType,YYEncodingTypeQualifier,YYEncodingTypeProperty)的组合。该变量进行和其中YYEncodingTypeMask取&操作可以取得低8位的值也就是YYEncodingType类型的值通过这种方法就实现了三个状态枚举值得组合与获取具体的哪种枚举值。
YYEncodingType YYEncodingGetType(const char *typeEncoding); 该方法是获取枚举值的方法具体看实现文件

YYEncodingType YYEncodingGetType(const char *typeEncoding) {
    char *type = (char *)typeEncoding;
    //type为空则返回
    if (!type) return YYEncodingTypeUnknown;
    size_t len = strlen(type);
    //type长度为0,则返回
    if (len == 0) return YYEncodingTypeUnknown;
    
    /*** 1.获取YYEncodingTypeQualifier类型值 ***/
    //用来存储枚举类型的变量
    YYEncodingType qualifier = 0;
    //循环结束控制变量
    bool prefix = true;
    //一下代码用来获取YYEncodingTypeQualifier类型的值存储在qualifier变量里
    while (prefix) {
        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;
        }
    }

    /*** 2. 获取YYEncodingType类型值 ***/
    len = strlen(type);
    //type长度为0则返回未知类型
    if (len == 0) return YYEncodingTypeUnknown | qualifier;
    //一下代码用来获取YYEncodingType类型的枚举值,存贮在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;
            else
                return YYEncodingTypeObject | qualifier;
        }
        default: return YYEncodingTypeUnknown | qualifier;
    }
}

解析:以上代码已经加了注释。该方法就是用来获取YYEncodingTypeQualifier 和 YYEncodingType枚举值的组合。(接下来用YYEncodingTypeMask,YYEncodingTypeQualifierMask即可获取具体的值)

YYClassIvarInfo模块

@interface YYClassIvarInfo : NSObject
@property (nonatomic, assign, readonly) Ivar ivar;              ///< ivar opaque struct
@property (nonatomic, strong, readonly) NSString *name;         ///< Ivar's name
@property (nonatomic, assign, readonly) ptrdiff_t offset;       ///< Ivar's offset
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
@property (nonatomic, assign, readonly) YYEncodingType type;    ///< Ivar's type

/**
 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

解析:该.h文件主要是用几个属性来存储成员变量的几个具体的值。利用- (instancetype)initWithIvar:(Ivar)ivar方法来实现几个属性的赋值操作,这样就得到了该成员变量的具体信息了。接下来看下具体的实现。

- (instancetype)initWithIvar:(Ivar)ivar {
    //ivar为空则返回nil
    if (!ivar) return nil;
    self = [super init];
    //保存ivar
    _ivar = ivar;
    //保存ivar的name
    const char *name = ivar_getName(ivar);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }
    //保存ivar的offset
    _offset = ivar_getOffset(ivar);
    //保存typeEncoding和type
    const char *typeEncoding = ivar_getTypeEncoding(ivar);
    if (typeEncoding) {
        _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
        _type = YYEncodingGetType(typeEncoding);
    }
    return self;
}

解析:以上代码已加注释,其中typeEncoding是苹果定义的一种类型编码,该编码记录了属性的类型。其实该编码可以记录所有类型,包括方法的返回值类型,入参等等。

YYClassMethodInfo模块

@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
@property (nonatomic, assign, readonly) IMP imp;                        ///< method's implementation
@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

/**
 Creates and returns a method info object.
 
 @param method method opaque struct
 @return A new object, or nil if an error occurs.
 */
- (instancetype)initWithMethod:(Method)method;
@end

解析:以上代码其实和ivar的类似,都是用几个属性来记录该属性的具体信息的,利用- (instancetype)initWithMethod:(Method)method方法实现具体的赋值,这样就记录了这个属性的具体信息了。接下来看下具体实现。

@implementation YYClassMethodInfo

- (instancetype)initWithMethod:(Method)method {
    //method为空则返回nil
    if (!method) return nil;
    self = [super init];
    //保存method
    _method = method;
    //保存sel
    _sel = method_getName(method);
    //保存imp(imp其实是方法的具体实现函数,记录着方法实现函数的地址)
    _imp = method_getImplementation(method);
    //保存name
    const char *name = sel_getName(_sel);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }
    //保存typeEncoding
    const char *typeEncoding = method_getTypeEncoding(method);
    if (typeEncoding) {
        _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
    }
    //保存returnType
    char *returnType = method_copyReturnType(method);
    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);
            NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
            [argumentTypes addObject:type ? type : @""];
            if (argumentType) free(argumentType);
        }
        _argumentTypeEncodings = argumentTypes;
    }
    return self;
}

@end

解析:以上代码中typeEncoding是记录着方法的所有参数类型,包括返回值的参数类型,和入参的参数类型。returnType来把typeEncoding来保存返回值类型,argumentTypes来保存方法的入参类型。

YYClassPropertyInfo模块

@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)

/**
 Creates and returns a property info object.
 
 @param property property opaque struct
 @return A new object, or nil if an error occurs.
 */
- (instancetype)initWithProperty:(objc_property_t)property;
@end

解析:和ivar,method一样都是用几个属性来保存属性信息,利用- (instancetype)initWithProperty:(objc_property_t)property方法来完成几个属性的赋值,来保存property信息。接下来看下具体的实现

- (instancetype)initWithProperty:(objc_property_t)property {
    //property为空则返回
    if (!property) return nil;
    self = [super init];
    //保存property
    _property = property;
    //保存name
    const char *name = property_getName(property);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }
    
    /*** 下边是对属性类型的保存 ***/
    YYEncodingType type = 0;
    unsigned int attrCount;
    objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
    for (unsigned int i = 0; i < attrCount; i++) {
        switch (attrs[i].name[0]) {
            case 'T': { // Type encoding
                if (attrs[i].value) {
                    //保存typeEncoding
                    _typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
                    type = YYEncodingGetType(attrs[i].value);
                    
                    if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
                        //保存class类型
                        NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
                        if (![scanner scanString:@"@\"" intoString:NULL]) continue;
                        
                        NSString *clsName = nil;
                        if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
                            if (clsName.length) _cls = objc_getClass(clsName.UTF8String);
                        }
                        
                        //保存protocol类型
                        NSMutableArray *protocols = nil;
                        while ([scanner scanString:@"<" intoString:NULL]) {
                            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
                if (attrs[i].value) {
                    _ivarName = [NSString stringWithUTF8String:attrs[i].value];
                }
            } break;
            case 'R': {
                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) {
        free(attrs);
        attrs = NULL;
    }
    
    _type = type;
    //保存getter,setter方法
    if (_name.length) {
        if (!_getter) {
            _getter = NSSelectorFromString(_name);
        }
        if (!_setter) {
            _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
        }
    }
    return self;
}

@end

解析:以上对属性的基本信息实现了存储

YYClassInfo模块

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

/**
 If the class is changed (for example: you add a method to this class with
 'class_addMethod()'), you should call this method to refresh the class info cache.
 
 After called this method, `needUpdate` will returns `YES`, and you should call 
 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info.
 */
- (void)setNeedUpdate;

/**
 If this method returns `YES`, you should stop using this instance and call
 `classInfoWithClass` or `classInfoWithClassName` to get the updated class info.
 
 @return Whether this class info need update.
 */
- (BOOL)needUpdate;

/**
 Get the class info of a specified Class.
 
 @discussion This method will cache the class info and super-class info
 at the first access to the Class. This method is thread-safe.
 
 @param cls A class.
 @return A class info, or nil if an error occurs.
 */
+ (nullable instancetype)classInfoWithClass:(Class)cls;

/**
 Get the class info of a specified Class.
 
 @discussion This method will cache the class info and super-class info
 at the first access to the Class. This method is thread-safe.
 
 @param className A class name.
 @return A class info, or nil if an error occurs.
 */
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;

@end

解析:以上属性都是class的基本信息,利用classInfoWithClass方法实现赋值来保存class的信息。
其中ivarInfos,methodInfos,propertyInfos三个都是字典里边的元素是YYClassIvarInfo,YYClassMethodInfo,YYClassPropertyInfo类型。也就是YYClassInfo保存了类的名字,父类,元类,还保存了该类的成员变量,属性,方法等信息。
setNeedUpdate方法是设置要更新该类的信息。needUpdate方法是返回是否需要更新该类的信息classInfoWithClassName是根据类名来获取该类的信息。接下来看下实现。

@implementation YYClassInfo {
    //用来判断是否需要更新类信息
    BOOL _needUpdate;
}

- (instancetype)initWithClass:(Class)cls {
    //cls为空则返回nil
    if (!cls) return nil;
    self = [super init];
    //保存cls
    _cls = cls;
    //保存superCls
    _superCls = class_getSuperclass(cls);
    //保存是否是元类
    _isMeta = class_isMetaClass(cls);
    if (!_isMeta) {
        //如果不是元类,那就去获取元类
        _metaCls = objc_getMetaClass(class_getName(cls));
    }
    //保存类名
    _name = NSStringFromClass(cls);
    //去更新类的信息(主要是ivar,method,property)
    [self _update];
    //保存父类信息
    _superClassInfo = [self.class classInfoWithClass:_superCls];
    return self;
}

/**
 该方法是来更新类的ivar,method,property信息
 */
- (void)_update {
    //首先三者赋值空
    _ivarInfos = nil;
    _methodInfos = nil;
    _propertyInfos = nil;
    
    //获取cls
    Class cls = self.cls;
    /*** 1.保存method信息 ***/
    unsigned int methodCount = 0;
    //获取methods列表
    Method *methods = class_copyMethodList(cls, &methodCount);
    if (methods) {
        //遍历methods列表
        NSMutableDictionary *methodInfos = [NSMutableDictionary new];
        _methodInfos = methodInfos;
        for (unsigned int i = 0; i < methodCount; i++) {
            //获取每个method的信息
            YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
            //用methodInfos去保存该method信息
            if (info.name) methodInfos[info.name] = info;
        }
        //释放methods对象(因为他是c的对象,需要我们手动释放)
        free(methods);
    }
    
    /*** 2.保存property信息 ***/
    unsigned int propertyCount = 0;
    //获取propertys列表
    objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
    if (properties) {
        NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
        _propertyInfos = propertyInfos;
        //遍历propertys列表
        for (unsigned int i = 0; i < propertyCount; i++) {
            //获取每个property的信息
            YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
            //用propertyInfos保存每个property信息
            if (info.name) propertyInfos[info.name] = info;
        }
        //释放prpperties对象(因为他是c的对象,需要我们手动释放)
        free(properties);
    }
    
    /*** 3.保存ivar信息 ***/
    unsigned int ivarCount = 0;
    //获取ivars列表
    Ivar *ivars = class_copyIvarList(cls, &ivarCount);
    if (ivars) {
        NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
        _ivarInfos = ivarInfos;
        //遍历ivar列表
        for (unsigned int i = 0; i < ivarCount; i++) {
            //获取每个ivar信息
            YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];
            //用ivarInfos保存每个ivar信息
            if (info.name) ivarInfos[info.name] = info;
        }
        //释放ivars对象(因为他是c的对象,需要我们手动释放)
        free(ivars);
    }
    
    if (!_ivarInfos) _ivarInfos = @{};
    if (!_methodInfos) _methodInfos = @{};
    if (!_propertyInfos) _propertyInfos = @{};
    //因为刚刚更新过ivar,method,property,所以_needUpdate变量赋值NO
    _needUpdate = NO;
}

- (void)setNeedUpdate {
    //将_needUpdate变量赋值YES,代表接下来要更新ivar,property,method信息
    _needUpdate = YES;
}

- (BOOL)needUpdate {
    //返回当前是否需要更新ivar,property,method信息
    return _needUpdate;
}

//根据Class来保存类信息
+ (instancetype)classInfoWithClass:(Class)cls {
    //cls为空则返回nil
    if (!cls) return nil;
    //单例创建classCache,metaCache字典(是存储类信息的字典)
    static CFMutableDictionaryRef classCache;
    static CFMutableDictionaryRef metaCache;
    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);
    //在线程安全下,判断当前类是不是元类,如果是元类就取metaCache的缓存。如果不是元类,就取classCache的缓存
    YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
    //如果取得了缓存,那就判断_needUpdate变量,是否需要更新,如果需要更新就去更新
    if (info && info->_needUpdate) {
        [info _update];
    }
    //这里相当于解锁操作
    dispatch_semaphore_signal(lock);
    if (!info) {
        //如果没有取得缓存,那就去创建该类信息
        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;
}
//根据ClassName来保存类信息
+ (instancetype)classInfoWithClassName:(NSString *)className {
    //根据className来获取class
    Class cls = NSClassFromString(className);
    //根据class来获取类信息
    return [self classInfoWithClass:cls];
}

@end

解析:该类实现代码较多点,我们来逐一分析。

  1. classInfoWithClassName方法和classInfoWithClass方法是入口方法。classInfoWithClassName方法内部在调用classInfoWithClass方法。所以classInfoWithClass方法是核心方法。
    这个方法内部有两个缓存字典classCache,metaCache,根据类名当做key去取缓存的类信息,如果取到了缓存类信息,去判断_needUpdate变量如果需要更新就去更新类信息(ivar,property,method信息)所有这里_update更新方法又是一个核心方法,一会我们再来解析。之后如果没有得到缓存的类信息,那就去创建类信息(这里调用了initWithClass方法,这个方法稍后再去解析),并缓存到对应的缓存字典里。最后返回该类信息对象。
    该入口方法里边有方法的嵌套,classInfoWithClass方法里边嵌套了initWithClass方法,initWithClass方法里边嵌套了classInfoWithClass方法,这样就在不停的调用方法classInfoWithClass去获取父类信息并缓存。结束调用的条件是到了根类了如NSObject的父类找不到即可结束循环。

  2. _update方法
    该方法内部首先先清空ivarinfos,_methodInfos,_propertyInfos,接下来在去赋值,起到了更新的作用。最后再把_needUpdate变量赋值为NO。该方法内部具体实现都已经加了注释。

  3. initWithClass方法
    该方法首先保存类的基本信息,然后再去调用_update方法更新ivar,property,method信息。这样当前类信息就已经有了(而且之后调用者会把当前类信息进行缓存)之后又调用了classInfoWithClass方法来保存父类的信息(也会进行缓存)。

总结

以上就是关于YYModel框架中的YYClassInfo类的源码解析。该类实现了类的基本信息的获取并进行了线程安全的缓存,使得调用起来速度更快更安全。从中我们队YYModel有了一定了解,同时学习了相关的runTime知识,相关runTime的方法见下篇文章。