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.htmlhttps://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<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

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

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<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *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<NSString *>
  • _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<YYModel>)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<YYModel>)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<YYModel>)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 函数表

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

  • <a id="YYNSBlockClass">YYNSBlockClass</a>:获取 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"
}
  • <a id="YYISODateFormatter">YYISODateFormatter</a>:获取 ISO 格式的 DateFormatter。
  • <a id="YYValueForKeyPath">YYValueForKeyPath</a>:对一个字典使用 keyPath 来取值。如 a.b 的形式。
  • <a id="YYValueForMultiKeys">YYValueForMultiKeys</a>:从给定的多个 key 中获取字典中的 value,如果是 key 是数组,那么会调用 YYValueForKeyPath 方法
  • <a id="ModelCreateNumberFromProperty">ModelCreateNumberFromProperty</a>:从给定的 _YYModelPropertyMeta 对象获取 NSNumber,如果失败,返回 nil 此方法使用 msg_send 来调用 getter 方法。
  • <a id="ModelSetNumberToProperty">ModelSetNumberToProperty</a>:对给定的 _YYModelPropertyMeta 对象设置 NSNumber,此方法使用 msg_send 来调用 getter 方法。
  • <a id="ModelSetValueForProperty">ModelSetValueForProperty</a>:对给定的 _YYModelPropertyMeta 设置一个 value,如果是数字类型,则调用 ModelSetNumberToProperty,如果属性的 type 和设置 value 的 type 不一样,则会尝试转换。调用 msg_send 来设置 setter 方法。
  • <a id="ModelSetWithDictionaryFunction">ModelSetWithDictionaryFunction</a>:传入 key 和 value 之后调用 ModelSetValueForProperty 来对一个 key 来设置 value。
  • <a id="ModelSetWithPropertyMetaArrayFunction">ModelSetWithPropertyMetaArrayFunction</a>:对于一个给定的 _propertyMeta 对象获取其的 mapper key,对于 keyPath 的调用 YYValueForKeyPath 来获取字典中的 value ,对多个 key 映射使用 YYValueForMultiKeys 来获取 value 之后调用 ModelSetValueForProperty 来设置属性的 value
  • <a id="ModelToJSONObjectRecursive">ModelToJSONObjectRecursive</a>:对于给定的 model 把自己的属性输出成 JSON 对象,如果是会自定义对象则递归的调用自己来输出。
  • <a id="ModelDescriptionAddIndent">ModelDescriptionAddIndent</a>:对于输出的字符串的缩进处理
  • <a id="ModelDescription">ModelDescription</a>:此方法实现 Description 宏,会调用 ModelToJSONObjectRecursive 来将对象转换成 NSString,之后在用 ModelDescriptionAddIndent 来处理缩进
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容