iOS 大神之路iOS知识收集源码解析

YYModel源码详细解析-1

2016-06-18  本文已影响2996人  js丶

前言:

阅读YYModel之前建议先阅读Runtime基础篇YYModel采用Runtime直接调用 Getter/Setter,是一款高性能 iOS/OSX 模型转换框架,支持定义映射过程。正好最近想深入Runtime一番,于是就读了几遍YYModel的源码,本文将从作者提供的性能优化的几个 Tip,还有结合Github 上的 Issues,以及相关项目线索为起点对YYModel源码进行分析,并且绘制一张项目的整体架构图,设计的思想。

源码来源:

YYModel

资料参考文献

Type Encodings
Declared Properties
Runtime参考文档

项目整体架构:

项目图

Paste_Image.png Paste_Image.png Paste_Image.png Paste_Image.png Paste_Image.png

![Uploading Paste_Image_669357.png . . .]

Paste_Image.png Paste_Image.png Paste_Image.png Paste_Image.png

类与对象的继承层次关系如图1-1

图1-1 Google source

YYModel层次关系

YYClassInfo层次关系

YYClassInfo设计图

注意:所指定的方向是包含关系,例如YYClassInfo包含class,同时YYClassInfo又包含YYClassMethodInfo、YYClassPropertyInfo、YYClassIvarInfo,其实用继承关系理解是最好,这样可以和类与关系图思想来理解更好(所以下面的图,就画成继承关系,便于结合类与关系继承理解),但是准确来说是包含关系。

_YYModelPropertyMeta层次关系

_YYModelPropertyMeta层次关系可以分为两种,一种是_YYModelPropertyMeta对YYClassInfo的封装,另一种是_YYModelPropertyMeta对YYClassPropertyInfo的封装。

** _YYModelPropertyMeta对YYClassInfo封装**

Class包含关系图 Paste_Image.png Paste_Image.png Paste_Image.png Paste_Image.png

** _YYModelPropertyMeta对YYClassPropertyInfo封装**

_YYModelPropertyMeta设计图1

YYClassInfo.h

类型编码

YYClassInfo提供了三种枚举类型分别是Foundation Framework 编码、 Qualifier限定符编码、 Property属性编码其对应类型编码文档Table 6-1、Table 6-2、Table 7-1.

typedef NS_OPTIONS(NSUInteger, YYEncodingType) {
    
typedef NS_OPTIONS(NSUInteger, YYEncodingType) {
    
    // Table 6-1 Foundation Framework 编码
    YYEncodingTypeMask       = 0xFF, ///< mask of type value  表示低8位的十六进制Mask掩码,用于得到枚举值的低8位值(0 --> 8位),对0xFF按位与运算,可以避免误差
    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)
    
    
    // Table 6-2 Qualifier限定符编码
    YYEncodingTypeQualifierMask   = 0xFF00,   ///< mask of qualifier 表示8 ~ 15位 的十六进制Mask掩码,用于得到枚举值的低15位值
    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
    
    
    // Table 7-1 Property属性编码
    YYEncodingTypePropertyMask         = 0xFF0000, ///< mask of property 表示16 ~ 24位 的十六进制Mask掩码,用于得到枚举值的低24位值
    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
};

YYClassMethodInfo.h

YYModel采用Runtime直接调用 Getter/Setter,所以要将Runtime Method属性暴露出来提供MethodInfo类获取相应的属性
Runtime:Method information.

 Method information.
 
struct objc_method {

    SEL method_name                 OBJC2_UNAVAILABLE;  // 方法名

    char *method_types                  OBJC2_UNAVAILABLE;

    IMP method_imp                      OBJC2_UNAVAILABLE;  // 方法实现

}  

YYClassMethodInfo

@interface YYClassMethodInfo : NSObject
@property (nonatomic, assign, readonly) Method method; ///< method
@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
- (instancetype)initWithMethod:(Method)method;
@end

Runtime:Ivar information.

struct objc_ivar {
    char *ivar_name                 OBJC2_UNAVAILABLE;  // 变量名
    char *ivar_type                 OBJC2_UNAVAILABLE;  // 变量类型
    int ivar_offset                 OBJC2_UNAVAILABLE;  // 基地址偏移字节
#ifdef __LP64__
    int space                       OBJC2_UNAVAILABLE;
#endif

} 

** YYClassIvarInfo**

@interface YYClassIvarInfo : NSObject
@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar
@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
- (instancetype)initWithIvar:(Ivar)ivar;
@end

Runtime:Class information.

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__

    Class super_class                       OBJC2_UNAVAILABLE;  // 父类

    const char *name                        OBJC2_UNAVAILABLE;  // 类名

    long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0

    long info                               OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识

    long instance_size                      OBJC2_UNAVAILABLE;  // 该类的实例变量大小

    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 该类的成员变量链表

    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定义的链表

    struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法缓存

    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 协议链表

#endif
} OBJC2_UNAVAILABLE;

YYClassInfo.m

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

YYEncodingGetType方法
通过指定类型编码字符串,返回类型编码字符串中Foundation Framework 编码字符和method encodings编码字符

// 通过指定类型编码字符串,返回类型编码字符串中Foundation Framework 编码字符和method encodings编码字符
YYEncodingType YYEncodingGetType(const char *typeEncoding) {
    // 转换const限定符
    char *type = (char *)typeEncoding;
    // 传入字符串为NULL,返回未知类型
    if (!type) return YYEncodingTypeUnknown;
    size_t len = strlen(type);
    // 类型编码字符串的长度为空,返回未知类型编码
    if (len == 0) return YYEncodingTypeUnknown;
    // @property (nonatomic(3:property), copy(2:property)) const(0:Qualifier) NSString(1:Foundation Framework ) *title(4);   rT@"NSString",C,N,V_title
    YYEncodingType qualifier = 0;
    bool prefix = true;
    // 可能多个编码字符(多种方法修饰)
    while (prefix) {
        /**  for type qualifiers(方法编码限定符,其中switch对应类型编码文档:)
         Code Meaning
         r    const
         n     in
         N    inout
         o     out
         O    bycopy
         R    byref
         V    oneway
         */
        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;
            // bycopy 修饰的方法
            case 'R': {
                qualifier |= YYEncodingTypeQualifierByref;
                type++;
            } break;
            // oneway 修饰的方法
            case 'V': {
                qualifier |= YYEncodingTypeQualifierOneway;
                type++;
            } break;
            // 当前字符不再匹配 method encodings编码字符
            default: { prefix = false; } break;
        }
    }
    // 判断类型编码后续字符
    len = strlen(type);
    // 类型编码字符串的长度为空,返回字符串未知类型编码和method encodings限定符编码
    if (len == 0) return YYEncodingTypeUnknown | qualifier;

    switch (*type) {
        /** For Foundation Framework
         Code     Meaning
         c        A char
         i        An int
         s        A short
         l
         A long   l is treated as a 32-bit quantity on 64-bit programs.
         q        A long long
         C       An unsigned char
         I       An unsigned int
         S       An unsigned short
         L       An unsigned long
         Q       An unsigned long long
         f       A float
         d       A double
         B       A C++ bool or a C99 _Bool
         v       A void
         *       A character string (char *)
         @       An object (whether statically typed or typed id)
         #       A class object (Class)
         :       A method selector (SEL)
         [array type]  An array
         {name=type...}  A structure
         (name=type...)  A union
         bnum            A bit field of num bits
         ^type           A pointer to type
         ?      An unknown type (among other things, this code is used for function pointers)
         
         */
        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;
        }
        // 当前字符不再匹配 Foundation Framework 编码字符
        default: return YYEncodingTypeUnknown | qualifier;
    }
}

类型编码

实践一番类型编码看看编码具体格式,其中取出几个实例变量的类型编码讲解
LTBook.h

@interface LTBook : NSObject

@property (copy, nonatomic) NSString *name;
@property (nonatomic, assign) CGSize size;
@property NSString *desc;
@property (nonatomic, strong) NSDictionary *likedUsers; //
@property (nonatomic, strong) UIColor *color;
@property (assign, nonatomic) uint64_t pages;
@property (strong, nonatomic) NSDate *publishDate;
@property (readonly, copy, nonatomic) id idReadonlyCopyNonatomic;
@property (readonly, copy, nonatomic)  NSString const *strName;
@property (readonly, copy, atomic)  NSString *strName2;

@end

initWithIvar方法
该方法获得相应的Ivar属性,存入YYClassiVarInfo对应的属性,这里就不多说了

initWithMethod方法
该方法获得相应的Method属性,存入YYClassiVarInfo对应的属性

- (instancetype)initWithMethod:(Method)method {
    if (!method) return nil;
    self = [super init];
    _method = method;
    _sel = method_getName(method);
    _imp = method_getImplementation(method);
    const char *name = sel_getName(_sel);
    if (name) {
        _name = [NSString stringWithUTF8String:name]; // 在所有Runtime 以char *定义的API都被视为UTF-8编码,所以这里用stringWithUTF8String
    }
    const char *typeEncoding = method_getTypeEncoding(method);
    if (typeEncoding) {
        _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
    }
    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;
}

initWithProperty方法

- (instancetype)initWithProperty:(objc_property_t)property {
    if (!property) {
        return nil;
    }
    self = [self init];
    _property = property;
    const char *name = property_getName(property);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }
    
    LTEncodingType type = 0;
    unsigned int attrCount;
    objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
    
    for (unsigned int i = 0; i < attrCount; i++) {
        NSLog(@"attrs[i].name1[0]---%c",attrs[i].name[0]);
        switch (attrs[i].name[0]) { // 或者属性每一个类型编码,见声明属性文档:Declared property type encodings章节
            
            case 'T': { // Type encoding T@"NSString",C,N,V_name
                if (attrs[i].value) {  // attrs[i].value : Foundation Framework  Code; Example: attrs[i].name[0] = T,attrs->value = @? / @"NSString"/ @ / @"NSDate" / Q / @"UIColor"
                    _typeEncoding = [NSString stringWithUTF8String:attrs[i].value]; // NSString
                    
                    type = LTEncodingGetType(attrs[i].value); // attrs[i].value =  @"NSString" 此时传进去的@"NSString" 通过LTEncodingGetType方法获得对应  对应限定符 LTEncodingTypeObject
                    if ((type & LTEncodingTypeMask) == LTEncodingTypeObject) {
                        size_t len = strlen(attrs[i].value); // @"NSString" 长度则为11
                        if (len > 3) { // 一般属性的类型长度大于3
                            char name[len - 2]; //原来是name[11] ,现在 11 - 2   name[9]
                            name[len - 3] = '\0'; // name[9] = '\0' 设置最后索引为'\0' ,目的是去掉最有一个“
                            memcpy(name, attrs[i].value + 2, len - 3); // 这行代码目的是获得属性名, 猜想:attrs[i].value + 2 = NSString",属性类型和属性名是共享一份内存的,这个方法copy一份内存到name,通过name就可以获得对应的属性定义的类型。
                            _cls = objc_getClass(name); // name所指向的具体来说内存是属性定义的类型 这里通过name  获得int 、NSString
                        }
                    }
                }
            } break;
                // @property (nonatomic(3:property), copy(2:property)) const(0:Qualifier) NSString(1:Foundation Framework ) *title(4); rT@"NSString",C,N,V_title  获得 2 3 4 位置的编码类型
            case 'V': { // Instance variable
                if (attrs[i].value) {
                    _ivarName = [NSString stringWithUTF8String:attrs[i].value];
                }
            } break;
            case 'R': {
                type |= LTEncodingTypePropertyReadonly;
            } break;
            case 'C': {
                type |= LTEncodingTypePropertyCopy;
            } break;
            case '&': {
                type |= LTEncodingTypePropertyRetain;
            } break;
            case 'N': {
                type |= LTEncodingTypePropertyNonatomic;
            } break;
            case 'D': {
                type |= LTEncodingTypePropertyDynamic;
            } break;
            case 'W': {
                type |= LTEncodingTypePropertyWeak;
            } break;
            case 'G': {  // For Example:@property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter;  Save _getter/ _setter
                type |= LTEncodingTypePropertyCustomGetter;
                if (attrs[i].value) {
                    
                    _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
                }
            } break;
            case 'S': {
                type |= LTEncodingTypePropertyCustomSetter;
                if (attrs[i].value) {
                    _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
                }
            } break;
            default: break;
        }
    }
    
    if (attrs) {
        free(attrs);
        attrs = NULL;
    }
    
    _type = type;
    if (_name.length) {
        if (!_getter) {
            _getter = NSSelectorFromString(_name);
        }
        if (!_setter) { // if _setter not nil ,set Function name
            /**
             For Example: name =  _title  从t开始,而且t字母表示为大写(uppercaseString用字符串的大写字母标识)(substringToIndex取索引为1的字符,substringFromIndex:取1~~~~(length - 1) 字符)大写T
             */
            _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
        }
    }
    return self;
}

initWithClass/classInfoWithClass方法

- (instancetype)initWithClass:(Class)cls {
    // 判断参数的合法性
    if (!cls) return nil;
    self = [super init];
    _cls = cls;
    _superCls = class_getSuperclass(cls); // 获得父类
    _isMeta = class_isMetaClass(cls);   // 是否有元组
    if (!_isMeta) {
        _metaCls = objc_getMetaClass(class_getName(cls));   // 获得元组
    }
    _name = NSStringFromClass(cls);  // // 获得类名
    [self _update];  // 更新方法列表、属性列表、成员列表数据

    _superClassInfo = [self.class classInfoWithClass:_superCls]; // 递归最上层父亲信息
    return self;
}


+ (instancetype)classInfoWithClass:(Class)cls {
// 判断传入值的合法性
if (!cls) return nil;
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);
    /** dispatch_semaphore_create
     创建新的计数信号量的初始值。
     */
    lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
LTClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); // 判断该类是元组还是类(initWithClass方法中倒数第二行代码有调用classInfoWithClass方法传入的参数是元组,所以这里需要判断数据存入的是类换成年期还是元组缓存器),根据类名从缓存器中取出数据
if (info && info->_needUpdate) {
    [info _update];
}
dispatch_semaphore_signal(lock);
if (!info) { // 缓存器中没有该类信息,创建一个类信息
    info = [[LTClassInfo alloc] initWithClass:cls];
    if (info) {
        // 首先CFDictionaryRef是线程安全,这里加锁目的是为了保证内部数据的线程安全,所有访问CFDictionaryRef接口都要经过这个 lock
        dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); // 设置信号量为1,相当于添加同步锁
        // 缓存classInfo
        CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
        
        // 释放锁
        dispatch_semaphore_signal(lock);
    }
}
return info;
}

+ (instancetype)classInfoWithClass:(Class)cls {
    // 判断传入值的合法性
    if (!cls) return nil;
    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);
        /** dispatch_semaphore_create
         创建新的计数信号量的初始值。
         */
        lock = dispatch_semaphore_create(1);
    });
    dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    LTClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); // 判断该类是元组还是类(initWithClass方法中倒数第二行代码有调用classInfoWithClass方法传入的参数是元组,所以这里需要判断数据存入的是类换成年期还是元组缓存器),根据类名从缓存器中取出数据
    if (info && info->_needUpdate) {
        [info _update];
    }
    dispatch_semaphore_signal(lock);
    if (!info) { // 缓存器中没有该类信息,创建一个类信息
        info = [[LTClassInfo alloc] initWithClass:cls];
        if (info) {
            // 首先CFDictionaryRef是线程安全,这里加锁目的是为了保证内部数据的线程安全,所有访问CFDictionaryRef接口都要经过这个 lock
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); // 设置信号量为1,相当于添加同步锁
            // 缓存classInfo
            CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
            
            // 释放锁
            dispatch_semaphore_signal(lock);
        }
    }
    return info;
}

NSObject+YYModel

/// Foundation Class Type(Foundation框架类的类型)
typedef NS_ENUM (NSUInteger, YYEncodingNSType) {
    YYEncodingTypeNSUnknown = 0,
    YYEncodingTypeNSString,
    YYEncodingTypeNSMutableString,
    YYEncodingTypeNSValue,
    YYEncodingTypeNSNumber,
    YYEncodingTypeNSDecimalNumber,
    YYEncodingTypeNSData,
    YYEncodingTypeNSMutableData,
    YYEncodingTypeNSDate,
    YYEncodingTypeNSURL,
    YYEncodingTypeNSArray,
    YYEncodingTypeNSMutableArray,
    YYEncodingTypeNSDictionary,
    YYEncodingTypeNSMutableDictionary,
    YYEncodingTypeNSSet,
    YYEncodingTypeNSMutableSet,
};



/// Get the Foundation class type from property info.根据Class对象获得对应的Foundation类
static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {
    if (!cls) return YYEncodingTypeNSUnknown;  
    if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;
    if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
    if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;
    if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
    if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
    if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;
    if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
    if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;
    if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;
    if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;
    if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
    if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;
    if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
    if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;
    if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;
    return YYEncodingTypeNSUnknown;
}

** isKindOfClass、isMemberOfClass、isSubclassOfClass区别**
- (BOOL)isKindOfClass:(Class)aClass;
返回一个布尔值,该值指示class是一个给定的类的实例或者继承自该类的任何类的实例。
如果接收机是当前类实例或者类继承类的实例返回YES,否则返回NO
- (BOOL)isMemberOfClass:(Class)aClass;
返回一个布尔值,该值指示class是否是给定类的实例。
如果接收机是一类的一个实例返回YES,否则返回NO
+ (BOOL)isSubclassOfClass:(Class)aClass;
返回一个布尔值,该值指示class是否是该类的子类,或相同的一个给定的类。

** 为什么用内联函数来判断参数传入的类**
作者:尽量用纯 C 函数、内联函数,使用纯 C 函数可以避免 ObjC 的消息发送带来的开销,如果 C 函数比较小,使用 inline 可以避免一部分压栈弹栈等函数调用的开销。

/// Whether the type is c number.(判断类型编码是否C语言结构的类型)
static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
    switch (type & YYEncodingTypeMask) {
        case YYEncodingTypeBool:
        case YYEncodingTypeInt8:
        case YYEncodingTypeUInt8:
        case YYEncodingTypeInt16:
        case YYEncodingTypeUInt16:
        case YYEncodingTypeInt32:
        case YYEncodingTypeUInt32:
        case YYEncodingTypeInt64:
        case YYEncodingTypeUInt64:
        case YYEncodingTypeFloat:
        case YYEncodingTypeDouble:
        case YYEncodingTypeLongDouble: return YES;
        default: return NO;
    }
}

YYNSNumberCreateFromID方法

Paste_Image.png

三种格式

/// Parse a number value from 'id'.  (1)id类型对象 -》 NSNumber对象 (2) id类型对象 -》NSString类型对象 -》NSNumber类型对象
static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) {
    static NSCharacterSet *dot;
    static NSDictionary *dic;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ // 传入的值属性格式1对其进行处理
        dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)];
        // KEY-Value
        dic = @{@"TRUE" :   @(YES),
                @"True" :   @(YES),
                @"true" :   @(YES),
                @"FALSE" :  @(NO),
                @"False" :  @(NO),
                @"false" :  @(NO),
                @"YES" :    @(YES),
                @"Yes" :    @(YES),
                @"yes" :    @(YES),
                @"NO" :     @(NO),
                @"No" :     @(NO),
                @"no" :     @(NO),
                @"NIL" :    (id)kCFNull,
                @"Nil" :    (id)kCFNull,
                @"nil" :    (id)kCFNull,
                @"NULL" :   (id)kCFNull,
                @"Null" :   (id)kCFNull,
                @"null" :   (id)kCFNull,
                @"(NULL)" : (id)kCFNull,
                @"(Null)" : (id)kCFNull,
                @"(null)" : (id)kCFNull,
                @"<NULL>" : (id)kCFNull,
                @"<Null>" : (id)kCFNull,
                @"<null>" : (id)kCFNull};
    });
    // 判断参数属于格式1,直接返回nil
    if (!value || value == (id)kCFNull) return nil;
    // 判断参数类型是否标识数字类型NSNumber,属于格式2,无需处理直接返回值
    if ([value isKindOfClass:[NSNumber class]]) return value;
    // 判断参数是表示字符串NSString
    if ([value isKindOfClass:[NSString class]]) {
        NSNumber *num = dic[value];
        if (num) {
            if (num == (id)kCFNull) return nil;
            return num;
        }
        // 如果传入的value值是5788.57,  rangeOfCharacterFromSet查找字符串是否包含 `.`字符
        if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {
            const char *cstring = ((NSString *)value).UTF8String; // 因为要调用atof方法所以将value的类型转换为UTF-8类型
            if (!cstring) return nil;
            double num = atof(cstring); // double atof(const char *nptr); 将数字从NSString转化为double
            if (isnan(num) || isinf(num)) return nil;
            return @(num);
        } else { // value传入的值是5788转换为NSNumber数字类型
            const char *cstring = ((NSString *)value).UTF8String;
            if (!cstring) return nil;
            return @(atoll(cstring));  //  long long atoll(const char *nptr); 把字符串转换成长长整型数(64位)
        }
    }
    
    return nil;
}

**YYNSDateFromString方法 **

/// Parse string to date.
static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
    typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
    #define kParserNum 34 // 日期字符串的最大长度,实际
    // 保存对应长度长度日期字符串解析的Block数组,设置数组内值都为0,使用静态来保存解析成NSDate的Block并且设置对应日期字符串长度
    static YYNSDateParseBlock blocks[kParserNum + 1] = {0};
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        {
            /*
             2014-01-20  // Google
             */
            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
            formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
            formatter.dateFormat = @"yyyy-MM-dd";
            // 日期的字符串长度为10
            blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; };
        }
        
        {
            /*
             格式1:2014-01-20 12:24:48
             格式2:2014-01-20T12:24:48   // Google
             格式3:2014-01-20 12:24:48.000
             格式4:2014-01-20T12:24:48.000
             */
            
            /** 长度为19的日期字符串解析,分两种
             */
            NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
            formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
            formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";
            
            NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
            formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
            formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss";

            NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
            formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
            formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS";

            NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
            formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
            formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
            // 将日期字符串解析的Block保存到19的数组位置
            blocks[19] = ^(NSString *string) {
                if ([string characterAtIndex:10] == 'T') { // 格式2
                    return [formatter1 dateFromString:string];
                } else { // 格式1
                    return [formatter2 dateFromString:string];
                }
            };
            // 将日期字符串解析的Block保存到23的数组位置
            blocks[23] = ^(NSString *string) {
                if ([string characterAtIndex:10] == 'T') { // 格式3
                    return [formatter3 dateFromString:string];
                } else { // 格式4
                    return [formatter4 dateFromString:string];
                }
            };
        }
        
        {
            /*
             格式1:2014-01-20T12:24:48Z        // Github, Apple
             格式2:2014-01-20T12:24:48+0800    // Facebook
             格式3:2014-01-20T12:24:48+12:00   // Google
             格式4:2014-01-20T12:24:48.000Z
             格式5:2014-01-20T12:24:48.000+0800
             格式6:2014-01-20T12:24:48.000+12:00
             */
            NSDateFormatter *formatter = [NSDateFormatter new];
            formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

            NSDateFormatter *formatter2 = [NSDateFormatter new];
            formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ";
            // 将日期字符串解析的Block保存到20的数组位置,格式1
            blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; };
            // 将日期字符串解析的Block保存到24的数组位置,格式2
            blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
            // 将日期字符串解析的Block保存到25的数组位置,格式3
            blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; };
            // 将日期字符串解析的Block保存到28的数组位置,格式4
            blocks[28] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
            // 将日期字符串解析的Block保存到29的数组位置,格式5
            blocks[29] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
        }
        
        {
            /*
             格斯1:Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter
             格式2:Fri Sep 04 00:12:21.000 +0800 2015
             */
            NSDateFormatter *formatter = [NSDateFormatter new];
            formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";

            NSDateFormatter *formatter2 = [NSDateFormatter new];
            formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy";
            // 将日期字符串解析的Block保存到30的数组位置,格式1
            blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; };
            // 将日期字符串解析的Block保存到34的数组位置,格式2
            blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
        }
    });
    // 判断传入值合法性
    if (!string) return nil;
    // 如果传入字符串长度越界,不属于转换的范围内,直接返回nil
    if (string.length > kParserNum) return nil;
    // 设置block数组大小
    YYNSDateParseBlock parser = blocks[string.length];
    if (!parser) return nil;
    return parser(string);
    #undef kParserNum
}

**YYNSBlockClass方法 **

/// Get the 'NSBlock' class.获得NSBlock类
static force_inline Class YYNSBlockClass() {
    static Class cls;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        void (^block)(void) = ^{};
        cls = ((NSObject *)block).class; // 获得当前Block的Class
        while (class_getSuperclass(cls) != [NSObject class]) { // 遍历Block(__NSGlobalBlock__ - > __NSGlobalBlock - > NSBlock - > NSObject)最终的superClass,NSBlock的父类是NSObject
            
            cls = class_getSuperclass(cls);
        }
    });
    return cls; // current is "NSBlock"
}

**YYISODateFormatter **

/**
 Get the ISO date formatter. 获得ISO日期格式
 ISO:国际标准化组织的国际标准ISO 8601是日期和时间的表示方法
 ISO8601 format example:
 2010-07-09T16:13:30+12:00
 2011-01-11T11:11:11+0000
 2011-01-26T19:06:43Z
 
 length: 20/24/25
 */
 static force_inline NSDateFormatter *YYISODateFormatter() {
    static NSDateFormatter *formatter = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 初始化formatter
        formatter = [[NSDateFormatter alloc] init];
        // 设置时区
        formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
        // 设置ISO日期格式
        formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
    });
    return formatter;
}

YYValueForKeyPath/YYValueForMultiKeys方法

/// Get the value with key paths from dictionary(从字典中获取关键路径的值)
/// The dic should be NSDictionary, and the keyPath should not be nil. (dic[keyPaths[i]],应该是NSDictionaty字典,否则返回nil。)
/**
 json格式见下面方法
 */


static force_inline id LTValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {
    id value = nil;
    for (NSUInteger i = 0, max = keyPaths.count; i < max; i++) {
        /** 这里的keyPaths可以这么理解,如果传入的是以下JSON数据,那么keypath.count为2 此时max = 2   
        1. i = 0 , 0 = i < 2 = max; 2. url = keyPaths[0] = keyPaths[i]
        3. http://example.com/1.png = value = dic[@"url"], 如果value不是NSDictionary类型直接返回
        4. 0 + 1 =  i + 1 < 2 = max
        5. i++,
        --------------------------
        1. i = 1, 1 = i < 2 = max; 2. desc = keyPath[1] = keyPaths[i]
        3. Happy~ = value = dic[@"desc"], 如果value不是NSDictionary类型直接返回
        4. 1 + 1 < i + 1 < 2 = max 返回该值
        */
        
        /**
        格式2 "photos" : [ 格式2
            {
                "url":"http://example.com/1.png\",
                "desc":"Happy~"
            },
            {
                "url":"http://example.com/2.png\",
                "desc":"Yeah!"
            }
         ]
         */
        value = dic[keyPaths[i]]; // 根据key取值,keypath
        if (i + 1 < max) { // 相当于NSArray数组倒数第二个key
            // 判断该值如果是字典,直接赋值,否则返回Nil,格式2内的格式6
            if ([value isKindOfClass:[NSDictionary class]]) {
                dic = value; //
            } else {
                return nil;
            }
        }
    }
    return value;
}

/// Get the value with multi key (or key path) from dictionary(从字典中获取多个键(或KeyPath值相当于字典)的值)
/// The dic should be NSDictionary(dic是一个字典)
/** json
 {
    "name" : "Happy Birthday",  格式1
    "photos" : [
        {
            "url":"http://example.com/1.png",
            "desc":"Happy~"
        },
        {
            "url":"http://example.com/2.png",
            "desc":"Yeah!"
        }
    ],
    "likedUsers" : { 格式1
            "Jony" : {"uid":10001,"name":"Jony"}, 格式3
            "Anna" : {"uid":10002,"name":"Anna"}, 格式3
            "desc":"Happy~", 格式4
    格式2    "photos" : [ 格式6
                {
                    "url":"http://example.com/1.png",
                    "desc":"Happy~"
                },
                {
                    "url":"http://example.com/2.png", 格式5
                    "desc":"Yeah!"
                }
            ]
    },
    "likedUserIds" : [10001,10002]
 }
 
 */
static force_inline id LTValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {// dic 为格式1
    id value = nil;
    for (NSString *key in multiKeys) { // 遍历multiKeys数组,依次取出key
        if ([key isKindOfClass:[NSString class]]) { // 如果是key字符类型,根据key从dic直接取出数据,格式1内存在格式4
            value = dic[key];
            if (value) break; // 如果根据key取出对应的值,停止继续取值
        } else { // 根据Key的值如果NSSArray,进入LTValueForKeyPath取出指定key的值,例如格式1内存在格式2
            value = LTValueForKeyPath(dic, (NSArray *)key);
            // 如果根据key取出对应的值,停止继续取值
            if (value) break;
        }
    }
    return value;
}

_YYModelPropertyMeta

Paste_Image.png Paste_Image.png
@interface _YYModelPropertyMeta : NSObject {
    @package
    NSString *_name;             ///< property's name 属性名称
    // 对象类型
    YYEncodingType _type;        ///< property's type  属性的基础类型
    // 对象类型
    YYEncodingNSType _nsType;    ///< property's Foundation type  属性的 Foundation Class类型
    BOOL _isCNumber;             ///< is c number type 是否是C语言结构类型
    //  实例变量来源于哪个类(可能是父类) */
    Class _cls;                  ///< property's class, or nil
    Class _genericCls;           ///< container's generic class, or nil if threr's no generic  容器的通用类,如果()没有通用class为nil,自定义类
    SEL _getter;                 ///< getter, or nil if the instances cannot respond,保存属性的getter方法
    SEL _setter;                 ///< setter, or nil if the instances cannot respond 保存属性的setter方法
    BOOL _isKVCCompatible;       ///< YES if it can access with key-value coding 类型是否不支持KVC
    BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver 结构是否支持 archiver(归档)/unarchiver(解档) 
    BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:  字典转模型是否类被实现
    
    /*
     property->key:       _mappedToKey:key     _mappedToKeyPath:nil            _mappedToKeyArray:nil
     property->keyPath:   _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
     property->keys:      _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath    _mappedToKeyArray:keys(array)
     */
    /**
     */
    NSString *_mappedToKey;      ///< the key mapped to  to key
    // 如果有多级映射   如果有多个属性映射到相同的键会用到
    NSArray *_mappedToKeyPath;   ///< the key path mapped to (nil if the name is not key path)
    NSArray *_mappedToKeyArray;  ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
    YYClassPropertyInfo *_info;  ///< property's info
    _YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.  下一个元,如果有多个属性映射到相同的键。
}
@end

_YYModelPropertyMeta属声明属性详细解析

@impleentation LMBook
+ (NSDictionary *)modelCustomPropertyMapper {
    return @{@"name"  : @"n",
             @"page"  : @"p",
             @"desc"  : @"ext.desc",
             @"bookID": @[@"id", @"ID", @"book_id"]};
}
@end
@{@"name"  : @"n",
  @"page"  : @"p"}
 json:
 {
    "n":"Harry Pottery",
    "p": 256,
 格式1"ext" : {
    "desc" : "A book written by J.K.Rowling."
    },
    "ID" : 100010
 }
@{@"name"  : @"n",
             @"page"  : @"p",
             @"desc"  : @"ext.desc",
       格式1  @"bookID": @[@"id", @"ID", @"book_id"]

metaWithClassInfo方法

// 根据YYClassInfo信息初始化YYClassInfo并且设置对应属性信息
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
    _YYModelPropertyMeta *meta = [self new];
    meta->_name = propertyInfo.name;
    meta->_type = propertyInfo.type;
    meta->_info = propertyInfo;
    meta->_genericCls = generic;
    
     // 如果属性的类型编码是对象,例如id, NSDate...,如果是对象类型那么一般是Foundation类型,直接根据属性的属性的类信息传入当前属性class信息或者对应Foundation类型
    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) { // 属性的 Foundation Class类型
        meta->_nsType = YYClassGetNSType(propertyInfo.cls);
    } else { // 属性是c语言基础类型
        meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
    }
    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) { // 属性是结构体类型
        /*
         It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
         iOS能够归档的struct类型有限制,能够使用归档的struct类型type encodings为如下:
         32 bit struct类型的 @encode()
         */
        static NSSet *types = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSMutableSet *set = [NSMutableSet new];
            // 32 bit
            [set addObject:@"{CGSize=ff}"];
            [set addObject:@"{CGPoint=ff}"];
            [set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
            [set addObject:@"{CGAffineTransform=ffffff}"];
            [set addObject:@"{UIEdgeInsets=ffff}"];
            [set addObject:@"{UIOffset=ff}"];
            
            /**
             iOS能够归档的struct类型有限制,能够使用归档的struct类型type encodings为如下:
             64 bit struct类型的 @encode()
             */
            // 64 bit
            [set addObject:@"{CGSize=dd}"];
            [set addObject:@"{CGPoint=dd}"];
            [set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
            [set addObject:@"{CGAffineTransform=dddddd}"];
            [set addObject:@"{UIEdgeInsets=dddd}"];
            [set addObject:@"{UIOffset=dd}"];
            types = set;
        });
        if ([types containsObject:propertyInfo.typeEncoding]) {
            meta->_isStructAvailableForKeyedArchiver = YES;
        }
    }
    meta->_cls = propertyInfo.cls;
    
    if (generic) { // 如果存在自定义映射
        meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)]; // 从传入的generic class读取自定义映射,设置该代理方法为第一响应
    } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) { // 属性类名不为空且不是Foundation类型
        meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];  // 从属性变量类型的Class读取自定义映射,并且设置该代理方法为第一响应
    }
    // 保存Property的getter
    if (propertyInfo.getter) {
        /**
        instancesRespondToSelector:被调用时,动态方法是有机会的首先为selector提供一个IMP,如果该类对应的Property有getter方法实现方法,则返回YES
         */
        if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
            meta->_getter = propertyInfo.getter; // 从属性列表获取响应属性的getter方法
        }
    }
    // 保存Property的setter
    if (propertyInfo.setter) {
        /**
         instancesRespondToSelector:被调用时,动态方法是有机会的首先为selector提供一个IMP,如果该类对应的Property有setter方法实现方法,则返回YES
         */
        if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
            meta->_setter = propertyInfo.setter;
        }
    }
    /** 属性变量是否支持KVC,有一个条件
     * getter/setter方法必须实现
     */
    if (meta->_getter && meta->_setter) {
        /*
         KVC invalid type:
         long double
         pointer (such as SEL/CoreFoundation object)
         */
        /** 有两种类型不支持KVC
         * 1.long double 不支持KVC
         * 2. pointer (such as SEL/CoreFoundation object) 不支持KVC
         */
        switch (meta->_type & YYEncodingTypeMask) {
            case YYEncodingTypeBool:
            case YYEncodingTypeInt8:
            case YYEncodingTypeUInt8:
            case YYEncodingTypeInt16:
            case YYEncodingTypeUInt16:
            case YYEncodingTypeInt32:
            case YYEncodingTypeUInt32:
            case YYEncodingTypeInt64:
            case YYEncodingTypeUInt64:
            case YYEncodingTypeFloat:
            case YYEncodingTypeDouble:
            case YYEncodingTypeObject:
            case YYEncodingTypeClass:
            case YYEncodingTypeBlock:
            case YYEncodingTypeStruct:
            case YYEncodingTypeUnion: {
                meta->_isKVCCompatible = YES;
            } break;
            default: break;
        }
    }
    
    return meta;
}

第一遍未修改版本,未完待续

上一篇下一篇

猜你喜欢

热点阅读