YYModel

简介

YYModel 是YY大神写的 JSON 转模型的库,其具有高性能,自动类型转换,类型安全,无侵入性,轻量等特点。

基于 Runtime 封装

OC Runtime 的数据结构如实例变量 ival, 方法 method,属性 property,类 Class 都是结构体,使用极其不便,YYmodel 对这个几个结构进行封装。

YYClassIvarInfo

此类是 OC 的 实例变量的封装

@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

此对象,通过 Ivar 来解析出实例变量的属性,如,变量名,偏移量,类型编码,还有类型。关于类型编码您可以看苹果的官方文档https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html 和 https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html,每个类型都有相应的类型编码。而 YYEncodingType 则是 YYModel 对于实例变量的类型,修饰符的枚举类型,其定义为

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
};

可以看出这里面对所有变量类型和修饰符(nonatomic和copy等修饰)都有创建了枚举。

此类的初始化通过一个 ival 变量来解析出所有相关实例变量的属性,通过ivar_getTypeEncoding方法来获取类型编码。然后通过YYEncodingGetType函数来解析出 YYEncodingType 。而YYEncodingGetType函数的思路是通过分析 typeEncoding 字符串来解析出修饰符和类型。

YYClassMethodInfo

YYClassMethodInfo 这个类则是对 runtime method的封装,里面有 method, name等属性,通过一个 method 来初始化

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

name 通过sel_getName获取,sel 通过method_getName获取,imp 通过method_getImplementation获取,returnType 则是通过method_copyReturnType获取,method_getNumberOfArguments可以获取参数个数,method_copyArgumentType可以获取每个参数的类型

YYClassPropertyInfo

YYClassPropertyInfo 是对属性 property 的封装,由一个 objc_property_t 变量来初始化

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

name 通过property_getName来获取,

通过property_copyAttributeList获取一个 property 所有的属性(objc_property_attribute_t 结构体),通过 objc_property_attribute_t 可以获取到 getter , setter, 修饰符。
获取协议

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;

通过对<>部分解析,我们可以得到属性的协议列表
关于属性的类型编码可以参考官方文档https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5

YYClassInfo

YYClassInfo 是对一个 Class 的高层封装,数据结构如

@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 *ivarInfos; ///< ivars
@property (nullable, nonatomic, strong, readonly) NSDictionary *methodInfos; ///< methods
@property (nullable, nonatomic, strong, readonly) NSDictionary *propertyInfos; ///< properties

在内部包括了当 父类,元类,YYClassIvarInfo , YYClassMethodInfo, YYClassPropertyInfo 等相关信息。

建立索引

_update为内部更新属性的方法,此方法重新生成 Class 的 ivarInfosmethodInfospropertyInfos信息。

建立ivarInfos

 unsigned int methodCount = 0;
    Method *methods = class_copyMethodList(cls, &methodCount);
    if (methods) {
        NSMutableDictionary *methodInfos = [NSMutableDictionary new];
        _methodInfos = methodInfos;
        for (unsigned int i = 0; i < methodCount; i++) {
            YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
            if (info.name) methodInfos[info.name] = info;
        }
        free(methods);
    }

建立methodInfos

 unsigned int propertyCount = 0;
    objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
    if (properties) {
        NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
        _propertyInfos = propertyInfos;
        for (unsigned int i = 0; i < propertyCount; i++) {
            YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
            if (info.name) propertyInfos[info.name] = info;
        }
        free(properties);
    }

建立propertyInfos

 unsigned int propertyCount = 0;
    objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
    if (properties) {
        NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
        _propertyInfos = propertyInfos;
        for (unsigned int i = 0; i < propertyCount; i++) {
            YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
            if (info.name) propertyInfos[info.name] = info;
        }
        free(properties);
    }

更新机制

当向一个类动态添加方法(class_addMethod()),需要更新一个类的信息。调用setNeedUpdate方法会把 _needUpdate设置为 YES, 在下一次获取一个 class 的 YYClassInfo 的时候会根据这个字段来调用_update来重新建立索引。

缓存

对一个类建立实例变量,属性,方法的索引之后,会对这个类进行缓存,以便下一次使用的时候不用重新建立索引,YYModel使用了 CFDictionaryCreateMutable 来进行缓存类的 info ,key 是类名,value 是 YYClassInfo 对象。使用 dispatch_semaphore_t 保证线程安全,之所以选择个是基于性能的考虑。

+ (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);
        lock = dispatch_semaphore_create(1);
    });
    dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
    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;
}

对映射处理的封装

_YYModelPropertyMeta

_YYModelPropertyMeta 是一个内部类,用来处理一个 Class 的 Property 的元数据类。和 YYClassPropertyInfo 不同的是,_YYModelPropertyMeta 还负责映射属性的维护。可以说是,对 YYClassPropertyInfo 更高层的封装,服务于映射的功能。

数据结构

 NSString *_name;             ///< property's name
    YYEncodingType _type;        ///< property's type
    YYEncodingNSType _nsType;    ///< property's Foundation type
    BOOL _isCNumber;             ///< is c number type
    Class _cls;                  ///< property's class, or nil
    Class _genericCls;           ///< container's generic class, or nil if threr's no generic class
    SEL _getter;                 ///< getter, or nil if the instances cannot respond
    SEL _setter;                 ///< setter, or nil if the instances cannot respond
    BOOL _isKVCCompatible;       ///< YES if it can access with key-value coding
    BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed 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
    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.

维护几个关于映射的属性

  • _name:属性的名字
  • _type:属性的类型
  • _nsType:属性的 NS类型,如 NSString,NSArray等系统类型
  • _isCNumber:时候是 C 语言的数值类型,如 int, double
  • _cls:属性所在的类
  • _genericCls:此属性指出当属性是容器时候的类型,如 NSArray
  • _getter和_setter: getter和setter方法
  • _isKVCCompatible:指出是否可以支持KVC
  • _isStructAvailableForKeyedArchiver:时候支持归档(服务于 YYModel 的 encode宏)
  • _hasCustomClassFromDictionary:是否支持对莫个字典使用特定模型。

下面几个是关于一个属性在 JSON 中映射的key的描述

  • _mappedToKey:在 JSON 的 key
  • _mappedToKeyPath:以 KeyPath 描述的key(@"desc" : @"ext.desc"
  • _mappedToKeyArray:映射多个 key 的信息存储(@"bookID" : @[@"id",@"ID",@"book_id"]
  • _info:YYClassPropertyInfo 对象,上文中介绍过
  • _next:如果多个属性映射到一个 key 的时候通过这个来查找。

初始化 _YYModelPropertyMeta

初始化_YYModelPropertyMeta通过静态方法+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic传入 propertyInfo 和 classInfo来完成初始化

流程大概如下
1.如果是属性是对象,通过 YYClassGetNSType 来从propertyInfo获取 NSType

  1. 判断属性是否可以归档。除了 CGSize ,CGPoint,CGRect,CGAffineTransform,UIEdgeInsets,UIOffset 其他都不支持归档
  2. 是否需要对字典设置单独的 class 通过类方法 modelCustomClassForDictionary 来返回特定的 model 类
  3. 判断是否支持 setter,和getter 方法。
  4. 判断时候支持 KVC。只有 long double 不支持KVC

_YYModelMeta

_YYModelMeta 是对一个需要映射的 Model 的封装。其数据结构为

 YYClassInfo *_classInfo;
    
    //这个是存储一个类最终的映射关系
    /// Key:mapped key and key path, Value:_YYModelPropertyMeta.
    NSDictionary *_mapper;
    /// Array<_YYModelPropertyMeta>, all property meta of this model.
    //所有需要处理的属性元数据
    NSArray *_allPropertyMetas;
    //嵌套类型的keypath的属性元数据
    /// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
    NSArray *_keyPathPropertyMetas;
    /// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
    //多key映射的的数组
    NSArray *_multiKeysPropertyMetas;
    /// The number of mapped key (and key path), same to _mapper.count.
    NSUInteger _keyMappedCount;
    /// Model class type.
    YYEncodingNSType _nsType;
    
    BOOL _hasCustomWillTransformFromDictionary;
    BOOL _hasCustomTransformFromDictionary;
    BOOL _hasCustomTransformToDictionary;
    BOOL _hasCustomClassFromDictionary;
  • _mapper:维护了所有的映射关系,key 是属性名,value 是 _YYModelPropertyMeta 对象。
  • _allPropertyMetas: 是所有映射属性 meta 的集合。
  • _keyPathPropertyMetas: 所有使用 keyPath 形式的属性集合
  • _multiKeysPropertyMetas: 所有多 key 映射的集合
  • _keyMappedCount: 映射的 key 的统计
  • _nsType: NS类型,如果是的话。

初始化过程

要生成一个 _YYModelMeta 需要 YYClassInfo 对象。
流程是:

  1. 获取黑名单和白名单。
  2. 创建 allPropertyMetas ,将所有属性添加到其中,根据黑,白名单剔除其中的元素。
  3. 将 allPropertyMetas 中的处理 keyPath 的 key 添加到 _keyPathPropertyMetas,将需要处理多 key 映射的添加到 _multiKeysPropertyMetas 中。

缓存机制

对于处理好的映射关系,YYModel 会对它们进行缓存,用的也是 CFDictionaryGetValue 来完成,使用 dispatch_semaphore_t 来保证线程安全

功能实现

JSON 转 Model

YYModel 使用方式简单,将转换方法写在了 NSObject 中的分类中,yy_modelWithJSON是转换的入口,yy_modelWithJSON中首先将 id 对象转换成字典,然后调用了yy_modelWithDictionary

   if (!dictionary || dictionary == (id)kCFNull) return nil;
    if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
    
    Class cls = [self class];
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
    if (modelMeta->_hasCustomClassFromDictionary) {
        cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
    }
    
    NSObject *one = [cls new];
    if ([one yy_modelSetWithDictionary:dictionary]) return one;
    return nil;

首先获取当前 class ,之后 new 出相应的对象,使用类方法yy_modelSetWithDictionary来将字典映射成 Model

- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
    if (!dic || dic == (id)kCFNull) return NO;
    if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    

    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
    if (modelMeta->_keyMappedCount == 0) return NO;
    
    if (modelMeta->_hasCustomWillTransformFromDictionary) {
        dic = [((id)self) modelCustomWillTransformFromDictionary:dic];
        if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    }
    
    ModelSetContext context = {0};
    context.modelMeta = (__bridge void *)(modelMeta);
    context.model = (__bridge void *)(self);
    context.dictionary = (__bridge void *)(dic);
    
    
    if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
        //需要对keypath和多key做处理
        //对字典的key进行一一映射到model
        CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
        //处理嵌套的映射

        if (modelMeta->_keyPathPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
        //处理多个key的映射
        if (modelMeta->_multiKeysPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
    } else {
        //安全的,可以对所有属性进行映射。
        CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
                             CFRangeMake(0, modelMeta->_keyMappedCount),
                             ModelSetWithPropertyMetaArrayFunction,
                             &context);
    }
    
    //
    if (modelMeta->_hasCustomTransformFromDictionary) {
        return [((id)self) modelCustomTransformFromDictionary:dic];
    }
    return YES;

首先初始化 _YYModelMeta ,然后创建一个 ModelSetContext 结构体

typedef struct {
    void *modelMeta;  ///< _YYModelMeta
    void *model;      ///< id (self)
    void *dictionary; ///< NSDictionary (json)
} ModelSetContext;

使用 CFDictionaryApplyFunction 对字典和属性调用 ModelSetWithDictionaryFunction 进行一一映射,对于有 keyPath 和多个 key 映射的情况使用 CFArrayApplyFunction 调用 ModelSetWithPropertyMetaArrayFunction
来完成多值映射的功能

数据校验

完成映射之后调用 modelCustomTransformFromDictionary 来判断 dic 是否完成,如果是返回 NO,则这个流程失败,返回nil,如果返回 YES, 则代表成功,返回此对象。

  if (modelMeta->_hasCustomTransformFromDictionary) {
        return [((id)self) modelCustomTransformFromDictionary:dic];
    }
    return YES;
    
    
     NSObject *one = [cls new];
    if ([one yy_modelSetWithDictionary:dictionary]) return one;
    return nil;

Model 转 JSON

内部调用 ModelToJSONObjectRecursive
来完成,具体可以看链接。

Coding

Coding 调用的是 yy_modelEncodeWithCoder 主要思路是通过 Model 的 _YYModelMeta 获取其中的所有的属性集合 _allPropertyMetas ,循环遍历属性,如果是 C Number转换成 NSNumber ,如果是对象,直接 encode ,如果是 SEL 则转换成 NSString ,对于不能处理的类型直接抛出异常。

Copying

copying 调用 yy_modelCopy,实现方式先获取 _YYModelMeta,和 _allPropertyMetas。创建一个新对象,遍历 _allPropertyMetas 一一把值设置到新对象。

hash

遍历所有的属性,如果属性存在,把所有属性的值按位异或,所得的value就是hash。
如果此类没有属性,那么以这个对象的指针作为hash

equal

先判断2个对象的 hash,如果相同,在判断所有值是不是相同,如果相同则equal为YES,其他则为 NO

description

调用 ModelDescription 来完成功能.

YYModel 函数表

  • YYClassGetNSType:通过一个 class 获取其 NSType,如果不是NSType,返回 YYEncodingTypeNSUnknown
  • YYEncodingTypeIsCNumber:判断 YYEncodingType 是不是 c 语言数字类型,如 int,double
  • YYNSNumberCreateFromID:从一个对象创建并且返回 NSNumber,如果失败则返回 nil,此函数对一下字符串做了特殊处理
  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,
                @"" : (id)kCFNull,
                @"" : (id)kCFNull,
                @"" : (id)kCFNull};
  • YYNSDateFromString:从给定的一个时间字符串中获取 NSDate 对象。核心实现是通过判断时间字符串的长度,来判断时间的类型,如 Google , Github ,Facebook等的时间格式。

  • YYNSBlockClass:获取 NSBlock 的类型

static force_inline Class YYNSBlockClass() {
    static Class cls;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        void (^block)(void) = ^{};
        cls = ((NSObject *)block).class;
        while (class_getSuperclass(cls) != [NSObject class]) {
            cls = class_getSuperclass(cls);
        }
    });
    return cls; // current is "NSBlock"
}
  • YYISODateFormatter:获取 ISO 格式的 DateFormatter。
  • YYValueForKeyPath:对一个字典使用 keyPath 来取值。如 a.b 的形式。
  • YYValueForMultiKeys:从给定的多个 key 中获取字典中的 value,如果是 key 是数组,那么会调用 YYValueForKeyPath 方法
  • ModelCreateNumberFromProperty:从给定的 _YYModelPropertyMeta 对象获取 NSNumber,如果失败,返回 nil 此方法使用 msg_send 来调用 getter 方法。
  • ModelSetNumberToProperty:对给定的 _YYModelPropertyMeta 对象设置 NSNumber,此方法使用 msg_send 来调用 getter 方法。
  • ModelSetValueForProperty:对给定的 _YYModelPropertyMeta 设置一个 value,如果是数字类型,则调用 ModelSetNumberToProperty,如果属性的 type 和设置 value 的 type 不一样,则会尝试转换。调用 msg_send 来设置 setter 方法。
  • ModelSetWithDictionaryFunction:传入 key 和 value 之后调用 ModelSetValueForProperty 来对一个 key 来设置 value。
  • ModelSetWithPropertyMetaArrayFunction:对于一个给定的 _propertyMeta 对象获取其的 mapper key,对于 keyPath 的调用 YYValueForKeyPath 来获取字典中的 value ,对多个 key 映射使用 YYValueForMultiKeys 来获取 value 之后调用 ModelSetValueForProperty 来设置属性的 value
  • ModelToJSONObjectRecursive:对于给定的 model 把自己的属性输出成 JSON 对象,如果是会自定义对象则递归的调用自己来输出。
  • ModelDescriptionAddIndent:对于输出的字符串的缩进处理
  • ModelDescription:此方法实现 Description 宏,会调用 ModelToJSONObjectRecursive 来将对象转换成 NSString,之后在用 ModelDescriptionAddIndent 来处理缩进

你可能感兴趣的:(YYModel)