YYClassInfo源碼解析

概況

YYClassInfo做爲YYModel的核心類,是YYModel解析Json轉模型必不可少的部分,其實它主要是利用runTime來獲取class內部信息,從而爲json轉model提供了便利工具。學習YYClassInfo類有助於理解YYModel的工做原理,也有利於增強runTime的基本功。Let's get started.json

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便可獲取具體的值)bash

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信息。接下來看下具體的實現ui

- (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
複製代碼

解析:以上對屬性的基本信息實現了存儲this

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的方法見下篇文章。

相關文章
相關標籤/搜索