xml地图|网站地图|网站标签 [设为首页] [加入收藏]

YYModel源码分析,YYModel源码学习

来源:http://www.ccidsi.com 作者:最新解决方案 人气:174 发布时间:2020-04-20
摘要:枚举的拍卖 有关项目编码的切实可行细节请自行查阅文书档案,本文不做批注。在 YYModel的源码中,笔者利用了贰个枚举来对应分歧的连串,见名知意,方便在框架中利用: typedef NS_

枚举的拍卖

有关项目编码的切实可行细节请自行查阅文书档案,本文不做批注。在 YYModel 的源码中,笔者利用了贰个枚举来对应分歧的连串,见名知意,方便在框架中利用:

typedef NS_OPTIONS(NSUInteger, YYEncodingType) { YYEncodingTypeMask = 0xFF, ///< mask of type value YYEncodingTypeUnknown = 0, ///< unknown YYEncodingTypeVoid = 1, ///< void ...... YYEncodingTypeCArray = 22, ///< char[10] (for example) YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier YYEncodingTypeQualifierConst = 1 << 8, ///< const YYEncodingTypeQualifierIn = 1 << 9, ///< in ...... YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly YYEncodingTypePropertyCopy = 1 << 17, ///< copy ...... YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic};

我而不是想把具有类别编码贴出来看,所以做了简便易行。那么些枚举恐怕是多选的,所以利用了 NS_OPTIONS 而不是 NS_ENUM

能够看出该枚举既包涵了单选枚举值,也包涵了多选枚举值,怎样让它们互不影响?

小编通过YYEncodingTypeMask、YYEncodingTypeQualifierMask、YYEncodingTypePropertyMask 三个掩码将枚举值分为三有的,它们的值转变为二进制分别为:

0000 0000 0000 0000 1111 11110000 0000 1111 1111 0000 00001111 1111 0000 0000 0000 0000

然后,那三有个别其余枚举的值,适逢其会遍及在这里五个 mask 枚举的值分成的八个区间。在源码中,会看出如下代码:

YYEncodingType type;if ((type & YYEncodingTypeMask) == YYEncodingTypeVoid) {...}

通过二个 位与& 运算符,直接将超过 YYEncodingTypeMask 的值过滤掉,然后完毕单值比较。

那是二个代码手艺,挺有意思。

至于 Type-Encoding 调换 YYEncodingType 枚举的代码就不表明了,基本上依据官方文档来的。

在 YYClassInfo 文件中,能够看看有这么多少个类:

YYClassIvarInfoYYClassMethodInfoYYClassPropertyInfoYYClassInfo

很明白,他们是将 Ivar、Method、objc_property_t、Class 的有关新闻装进去,这样做一是方便使用,二是为着做缓存。

在源码中能够看出:操作 runtime 底层类型的时候,由于它们不受 ARC 自动管理内存,所以记得用完了释放(不过不要去放活 const 常量),释放早前切记推断该内部存款和储蓄器是不是存在防御意外crash。

大旨的调换进度相当粗略,不一一商量,上面建议一些值得注意的地点:

YYEncodingType

基于项目编码自定义了体系枚举,包括了三个部分

YYEncodingTypeMask : 0~8位的值,变量的数据类型
YYEncodingTypeQualifierMask : 8~贰11人的值,变量的办法类型
YYEncodingTypePropertyMask: 16~22个人的值,变量的属性类型

那边把枚举值分成三个部分,通过 枚举值 & 对应 Mask 收取对应的变量类型,区分分歧体系部分。YYEncodingGetType 是基于变量的数据类型编码值获取自定义YYEncodingType

YYModel源码阅读

YYClassInfo 结构

@interface YYClassInfo : NSObject@property (nonatomic, assign, readonly) Class cls; ///< class object@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class@property (nonatomic, strong, readonly) NSString *name; ///< class name@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties...

能够观看,Class 类的分子变量、属性、方法分别装入了多少个 hash 容器(ivarInfos/methodInfos/propertyInfos)。

superClassInfo 指向父类,早先化时框架会循环境与发展展查找,直至当前 Class 的父类不设有(NSObject 父类指针为 nil),那就疑似三个一面的链表,将有继续关系的类新闻全体串联起来。这么做的目标,正是为着 json 转模型的时候,相近把父类的属性名作为炫目的 key。起先化 YYClassInfo 的代码大概如下:

- (instancetype)initWithClass:cls { if  return nil; self = [super init]; ...//_update方法就是将当前类的成员变量列表、属性列表、方法列表转换放进对应的 hash [self _update];//获取父类信息。 classInfoWithClass: 是一个获取类的方法,里面有缓存机制,下一步会讲到 _superClassInfo = [self.class classInfoWithClass:_superCls]; return self;}

元类

其中有metaCls意味着了元类。那什么是元类呢?下边是一张杰出的类构造图

图片 1

isa.png

在OC中,种种实例对象都有三个isa指针,它指向了目的的class。而这几个class也相通有二个isa指针,它正是指向了它的元类,其实类也是二个指标,所以指标之于类的关系,就一定于类(类对象)之于其元类(类对象的类)的涉及。那元类有哪些用吗?大家都知晓在OC中调用方法有实例方法和类措施。我们调用实例方法,正是经过isa指针找到钦赐的class,查找存款和储蓄在class中的方法列表实践措施,所以元类的意义正是调用类方法时,通过寻觅保存在元类中的类措施施行办法的法力。那怎么不把持有办法都封存在类中,恐怕那样特别急速也省去能源吧,具体能够和蔼寻觅资料。

在YYClassInfo中,有一个_update主意,用来更新类中蕴藏的消息。

①先查看metaWithClass内部的流水生产线:

1)在metaWithClass中,初次创立model时从缓存获取meta退步,则调用initWithClass生成叁个YYModelMeta。

  (instancetype)modelWithDictionary:(NSDictionary *)dictionary {
    //...    
     _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
    dispatch_semaphore_signal(lock);
    if (!meta || meta->_classInfo.needUpdate) {
        meta = [[_YYModelMeta alloc] initWithClass:cls];
          //...
    }
    //...
}

2)在YYModelMeta的initWithClass中,先依照参数cls生成YYClassInfo类classInfo,用classInfo方便大家对数据管理。

3)轻便的model,会直接调用第四部(前文提起)。
此间最重若是把classInfo中的property音讯抽取,生成贰个YYModelPropertyMeta实例。然后一切存入allPropertyMetas数组中。插问一句,为啥property这么重大?因为它是我们的model提供对外的习性,对应了JSON数据中的辞书。是链接JSON数据和model的桥梁(我乱比喻~)。

4)非自定义的key值,直接遍历存入哈希表中

[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
        propertyMeta->_mappedToKey = name;
        propertyMeta->_next = mapper[name] ?: nil;
        mapper[name] = propertyMeta;
    }];

5)最终是一些布尔值成员变量的赋值。经过以上的步骤,大家中标的取得了modelMeta。
6#)那只是对最基本的JSON数据类型的深入分析,YYModelMeta的开头化方法里面还也有对自定义键值的处理,照旧值得我们去浓郁摸底的。本文临时先剖析到那。

质量的优化

平昔利用 objc_msgSend给指标发送音讯的频率要大于使用 KVC,能够在源码中看看小编但凡能够利用发送消息赋值管理的,都不会使用 KVC。

回去初步,有多少个办法是陆陆续续接纳的(当然包涵 NSArray 和 NSDictionary 中的延展方法):

  (nullable instancetype)yy_modelWithJSON:json;  (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;

这么些主意其实落脚点都在三个措施:

- yy_modelSetWithDictionary:(NSDictionary *)dic { if (!dic || dic == kCFNull) return NO; if (![dic isKindOfClass:[NSDictionary class]]) return NO;//通过 Class 获取 _YYModelMeta 实例 _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass]; ... /*使用 ModelSetContext 结构体将以下内容装起来:1、具体模型对象 2、通过模型对象的类 Class 转换的 _YYModelMeta 对象(modelMeta)3、json 转换的原始数据*/ ModelSetContext context = {0}; context.modelMeta = (__bridge void *)(modelMeta); context.model = (__bridge void *); context.dictionary = (__bridge void *); //执行转换 if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) { CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context); if (modelMeta->_keyPathPropertyMetas) { CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas, CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)), ModelSetWithPropertyMetaArrayFunction, &context); } 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); } ... return YES;}

那边运用 CF 框架下的函数是为升级施行效用。

至于 ModelSetWithPropertyMetaArrayFunctionModelSetWithDictionaryFunction 的兑现不复杂,非常的少剖析。

小编不粗心的提供了一部分工具方法方便开采者使用。

NSObject YYModel:

NSObject YYModel是YYModel的主导类,首要部分:

抑遏内联C函数:成效函数
私有类_YYModelPropertyMeta : 管理Model属性的数量, 类型, 映射的key,keyPath
私有类 _YYModelMeta :管理Model 数据,类型,存储 映射key,keypath,_YYModelPropertyMeta
NSObject NSArray NSDictionary (YYModel卡塔尔国 : 多少个分类,YYModel主体职能达成
YYModel 合同:扩充效率达成

2.学习希图

我们从点子调用的时序去询问它此中的法规。可是在自此边,我们需求做一些备选干活。盘算了一份objc4的源码,大家询问YYClassInfo时会用到部分学问。

层层随笔:YYCache 源码解析:一览亮点YYModel 源码剖判:关切品质YYAsyncLayer 源码分析:异步绘制YYImage 源码剖判:图片管理技巧YYWebImage 源码解析:线程管理与缓存战术

JSON 转 Model

  (instancetype)yy_modelWithJSON:(id)son {
    NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
    return [self yy_modelWithDictionary:dic];
}

传入的json可以是 NSDictionary, NSString , NSData_yy_dictionaryWithJSON 统一转变成词典

  (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
    //字典合法性校验
    if (!dictionary || dictionary == (id)kCFNull) return nil;
    if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;

    Class cls = [self class];
   //创建 model 元数据
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
   //自定义字典
    if (modelMeta->_hasCustomClassFromDictionary) {
        cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
    }
    // 根据dictionary 进行 model 属性赋值
    NSObject *one = [cls new];
    if ([one yy_modelSetWithDictionary:dictionary]) return one;
    return nil;
}

结构体 ModelSetContext 存储 modelMeta ,model, dic 作为 CFDictionaryApplyFunctionCFArrayApplyFunction 的context 参数,传递数据

- (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)) {
        //映射的key Count >= dic Count
        CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
        if (modelMeta->_keyPathPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
        if (modelMeta->_multiKeysPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
    } else {
         //映射的key Count < dic Count  
        CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
                             CFRangeMake(0, modelMeta->_keyMappedCount),
                             ModelSetWithPropertyMetaArrayFunction,
                             &context);
    }

    if (modelMeta->_hasCustomTransformFromDictionary) {
        return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
    }
    return YES;
}

调用CoreFoundation 的 CFDictionaryApplyFunction 和 CFArrayApplyFunction 回调自定义的 Apply function
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context)static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context)

基于得到的value ,model ,_propertyMeta 最终统一调用 下边方法,运用runtime的 objc_msgSend 设置model属性

static void ModelSetValueForProperty(__unsafe_unretained id model,
                                     __unsafe_unretained id value,
                                     __unsafe_unretained _YYModelPropertyMeta *meta)

自定义的CFDictionaryApplyFunction 的回调方法,CoreFoundation中的原回调函数
typedef void (*CFDictionaryApplierFunction)(const void *key, const void *value, void *context);

/**
 Apply function for dictionary, to set the key-value pair to model.

 @param _key     should not be nil, NSString.
 @param _value   should not be nil.
 @param _context _context.modelMeta and _context.model should not be nil.
 */
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
    ModelSetContext *context = _context;
    __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
    __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
    __unsafe_unretained id model = (__bridge id)(context->model);
    while (propertyMeta) {
        if (propertyMeta->_setter) {
            ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
        }
        propertyMeta = propertyMeta->_next;
    };
}

自定义的CFArrayApplyFunction 的回调方法,CoreFoundation中的原回调函数
typedef void (*CFArrayApplierFunction)(const void *value, void *context);

/**
 Apply function for model property meta, to set dictionary to model.

 @param _propertyMeta should not be nil, _YYModelPropertyMeta.
 @param _context      _context.model and _context.dictionary should not be nil.
 */
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
    ModelSetContext *context = _context;
    __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
    __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
    if (!propertyMeta->_setter) return;
    id value = nil;

    if (propertyMeta->_mappedToKeyArray) {
            //从dic中获取映射多个key的值,返回映射到的第一个值
        value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
    } else if (propertyMeta->_mappedToKeyPath) {
            //从dic中获取映射keypath的值
        value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
    } else {
            //从dic中获取映射key的值
        value = [dictionary objectForKey:propertyMeta->_mappedToKey];
    }

    if (value) {
            //Model 属性赋值
        __unsafe_unretained id model = (__bridge id)(context->model);
        ModelSetValueForProperty(model, value, propertyMeta);
    }
}

最后兑现model 属性赋值。 该办法比较长,首先对 meta的质量类型举行剖断,首要分为三类,

  • C基本数据类型
  • Foundation 类型
  • 任何品类,如 id, Class ,block,SEL等等
    基于项目获取对应value,

末尾都调用 ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value) 举办model 属性赋值

static void ModelSetValueForProperty(__unsafe_unretained id model,
                                     __unsafe_unretained id value,
                                     __unsafe_unretained _YYModelPropertyMeta *meta)

1.德姆o简单介绍: 唯有2个落到实处公文,NSObject YYModel 和 YYClassInfo

1.普通差不离映射,NSObject的扩大:
(instancetype)modelWithJSON:(id)json;
大家必要和煦评释json中的key值,key值的体系能够是别的项目,如嵌套叁个model。

2.自定义映射情势。要是json中的key值并非你指望的key值,能够选取以下格局做一个炫目:
(NSDictionary *)modelCustomPropertyMapper;
归来的字典中,value是json中的key值,词典的key是生成的model的属性。

3.model的一对扩张属性,包括:
CodingCopyinghashequal

品质合同的缓存

@implementation YYClassPropertyInfo- (instancetype)initWithProperty:(objc_property_t)property { ... NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];... 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;...}...

这里小编将品质的情商同样存款和储蓄起来,在后文种描述这几个左券的作用。

前言

YYModel 是二个iOS JSON模型转变库,和此外一些同类型库相比较,具备相比好的天性优势。本文子禽对YYModel的源码进行剖析,具体用法小编ibireme在github中有聊起。YYModel的目录结构相当粗略,唯有四个类, NSObject YYModelYYClassInfoYYClassInfo要害对根类NSObject 的 Ivar , Method, Property以及Class小编进行了包装,NSObject YYModel 是 NSObject的归类,扩充了有些JSON模型转变的秘籍。下图一张YYmodel的总体布局图:

图片 2

YYModel 结构图.png

ivar: 定义对象的实例变量
struct ivar_t {
#if __x86_64__
    // *offset was originally 64-bit on some x86_64 platforms.
    // We read and write only 32 bits of it.
    // Some metadata provides all 64 bits. This is harmless for unsigned 
    // little-endian values.
    // Some code uses all 64 bits. class_addIvar() over-allocates the 
    // offset for their benefit.
#endif
    int32_t *offset;
    const char *name;
    const char *type;
    // alignment is sometimes -1; use alignment() instead
    uint32_t alignment_raw;
    uint32_t size;

    uint32_t alignment() const {
        if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
        return 1 << alignment_raw;
    }
};
拷贝
- yy_modelCopy;

瞩目是深拷贝。

参谋资料

郑钦洪_:YYModel 源码历险记

_YYModelPropertyMeta (NSObject YYModel.m中)

如注释所说,那是叁个关于model的property新闻的类。需求介怀的一点是声称的成员变量列表末尾有贰个next指针,结合布局方法能够清楚该meta类的目的是用链表的格局结合在一道的。

/// A property info in object model.
@interface _YYModelPropertyMeta : NSObject {
    //...略
}

它独有八个构造方法,做的事是:把传播的classInfo,propertyInfo,generic(映射关系表State of Qatar等新闻全体组装到了YYModelPropertyMeta中。用来干啥吧?后文继续~

  (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo 
                        propertyInfo:(YYClassPropertyInfo *)propertyInfo 
                             generic:(Class)generic;

YYClassInfo 缓存

作者做了一个类新闻(YYClassInfo)缓存的体制:

  (instancetype)classInfoWithClass:cls { if  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;//读取缓存 dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass ? metaCache : classCache, (__bridge const void *);//更新成员变量列表、属性列表、方法列表 if (info && info->_needUpdate) [info _update]; dispatch_semaphore_signal;//若无缓存,将 Class 类信息转换为新的 YYClassInfo 实例,并且放入缓存 if  { info = [[YYClassInfo alloc] initWithClass:cls]; if  { dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *), (__bridge const void *); dispatch_semaphore_signal; } } return info;}

由于同贰个类的连锁新闻在程序运维时期经常是相仿的,所以接纳 classCache 和 metaCache 缓存已经由此 runtime 转换为 YYClassInfo 的 Class,保险不会重复员和转业换 Class 类新闻做无用功;考虑到 runtime 带给的动态天性,小编接纳了多个 bool 值判别是或不是须求更新成员变量列表、属性列表、方法列表,_update艺术正是双重赢得那么些音讯。

其一缓存机制能带动非常高的频率升高,是 YYModel 三个比较基本的操作。

有多少个值得注意和学习的地点:

  1. 动用 static 修饰局地变量提高其生命周期,而又不修改其功效域,保险在程序运转时期部分变量不会释放,又幸免了其余代码对该有的变量的拜访。
  2. 线程安全的伪造。在开始化 static 变量的时候,使用dispatch_once()承保线程安全;在读取和写入使用 dispatch_semaphore_t随机信号量保障线程安全。

在踏入宗旨业务以前,先介绍部分 NSObject YYModel.m 里面值得注意的工具方法。

在工具方法中,日常拜谒到如此四个宏来修饰函数:

#define force_inline __inline__ __attribute__((always_inline))

它的功效是挟持内联,因为运用 inline 关键字末了会不会内联依旧由编写翻译器决定。对于那一个逼迫内联的函数参数,笔者经常应用 __unsafe_unretained 来修饰,谢绝其引用计数 1,以调整和减弱内部存款和储蓄器开销。

Model 转 JSON

在那之中有效的JSON Object 只好是以下体系:
NSArray,NSDictionary,NSString,NSNumber,NSNull。

1.假若是NSDictionary,NSSet,NSArray 类型,递归调用此方法得到JSON Object
2.一旦是NSU帕杰罗L,NSAttributedString ,NSDate, NSData,做轻巧相应重临
3.依据Model类型创设modelMeta,取实例变量_mapper 获取具有属性名和`propertyMeta,再根据propertyMeta的 类型_type 得到对应的 value, 依照有无_mappedToKeyPath再进一层管理,赋值,最后决断有无自定义_hasCustomTransformToDictionary,重临最终转化结果

言必有据措施:

static id ModelToJSONObjectRecursive(NSObject *model) {

    if (!model || model == (id)kCFNull) return model;
    if ([model isKindOfClass:[NSString class]]) return model;
    if ([model isKindOfClass:[NSNumber class]]) return model;
    if ([model isKindOfClass:[NSDictionary class]]) {
        if ([NSJSONSerialization isValidJSONObject:model]) return model;
        NSMutableDictionary *newDic = [NSMutableDictionary new];
        [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
            NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
            if (!stringKey) return;
            id jsonObj = ModelToJSONObjectRecursive(obj);
            if (!jsonObj) jsonObj = (id)kCFNull;
            newDic[stringKey] = jsonObj;
        }];
        return newDic;
    }
    if ([model isKindOfClass:[NSSet class]]) {
        NSArray *array = ((NSSet *)model).allObjects;
        if ([NSJSONSerialization isValidJSONObject:array]) return array;
        NSMutableArray *newArray = [NSMutableArray new];
        for (id obj in array) {
            if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
                [newArray addObject:obj];
            } else {
                id jsonObj = ModelToJSONObjectRecursive(obj);
                if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
            }
        }
        return newArray;
    }
    if ([model isKindOfClass:[NSArray class]]) {
        if ([NSJSONSerialization isValidJSONObject:model]) return model;
        NSMutableArray *newArray = [NSMutableArray new];
        for (id obj in (NSArray *)model) {
            if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
                [newArray addObject:obj];
            } else {
                id jsonObj = ModelToJSONObjectRecursive(obj);
                if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
            }
        }
        return newArray;
    }
    if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
    if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
    if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
    if ([model isKindOfClass:[NSData class]]) return nil;

    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
    if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
    NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
    __unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
    [modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
        if (!propertyMeta->_getter) return;

        id value = nil;
        if (propertyMeta->_isCNumber) {
            value = ModelCreateNumberFromProperty(model, propertyMeta);
        } else if (propertyMeta->_nsType) {
            id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
            value = ModelToJSONObjectRecursive(v);
        } else {
            switch (propertyMeta->_type & YYEncodingTypeMask) {
                case YYEncodingTypeObject: {
                    id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                    value = ModelToJSONObjectRecursive(v);
                    if (value == (id)kCFNull) value = nil;
                } break;
                case YYEncodingTypeClass: {
                    Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                    value = v ? NSStringFromClass(v) : nil;
                } break;
                case YYEncodingTypeSEL: {
                    SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                    value = v ? NSStringFromSelector(v) : nil;
                } break;
                default: break;
            }
        }
        if (!value) return;

        if (propertyMeta->_mappedToKeyPath) {
            NSMutableDictionary *superDic = dic;
            NSMutableDictionary *subDic = nil;
            for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i  ) {
                NSString *key = propertyMeta->_mappedToKeyPath[i];
                if (i   1 == max) { // end
                    if (!superDic[key]) superDic[key] = value;
                    break;
                }

                subDic = superDic[key];
                if (subDic) {
                    if ([subDic isKindOfClass:[NSDictionary class]]) {
                        subDic = subDic.mutableCopy;
                        superDic[key] = subDic;
                    } else {
                        break;
                    }
                } else {
                    subDic = [NSMutableDictionary new];
                    superDic[key] = subDic;
                }
                superDic = subDic;
                subDic = nil;
            }
        } else {
            if (!dic[propertyMeta->_mappedToKey]) {
                dic[propertyMeta->_mappedToKey] = value;
            }
        }
    }];

    if (modelMeta->_hasCustomTransformToDictionary) {
        BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
        if (!suc) return nil;
    }
    return result;
}

5.回想(个人的下结论,仅供参考)

YYModel对外的API非常老妪能解,可是中间中度使用了CFFoundation框架,大批量用到了指针、布局体等C语言层面包车型地铁内容,都以指望在性能上海展览中心现更加好。它把类、元类湖南中国广播公司大不曾门户开放的源委门户开放,封装,方便了定制化的操作。
上学的进度中,需求对OC对象模型有较好的询问,对CFFoundation有料定的支配,那个都更能加深大家对OC这么语言的认识,很好!
全文只是个体的敞亮,分外的地点希望指正,交流!

将 id 类型调换为 NSNumber

static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) { static NSCharacterSet *dot; static NSDictionary *dic; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ dot = [NSCharacterSet characterSetWithRange:NSMakeRange]; dic = @{@"TRUE" : @, @"True" : @, @"true" : @, ... @"NIL" : kCFNull, @"Nil" : kCFNull, ... }); if (!value || value == kCFNull) return nil; if ([value isKindOfClass:[NSNumber class]]) return value; if ([value isKindOfClass:[NSString class]]) { NSNumber *num = dic[value]; if  { if (num == kCFNull) return nil; return num; } ... return nil;}

这里的更动管理的要紧是 NSString 到 NSNumber 的调换,由于服务端重返给前端的 bool 类型、空类型多样二种,这里运用了叁个hash 将装有的情事作为 key 。然后调换的时候一向从 hash 中取值,将寻觅功用最大化升高。

中间变量

@interface _YYModelMeta : NSObject {
    @package
    YYClassInfo *_classInfo;

    // key: 映射的 json key ,keyPath  value: _YYModelPropertyMeta 
    NSDictionary *_mapper;

    // model所有属性的PropertyMetas
    NSArray *_allPropertyMetas;

    //model所有映射son keyPath 属性 的PropertyMetas
    NSArray *_keyPathPropertyMetas;

    // model所有映射多个key 属性 的PropertyMetas
    NSArray *_multiKeysPropertyMetas;
    /// 需要映射的属性总个数
    NSUInteger _keyMappedCount;

    /// Model对应的Foundation 类型
    YYEncodingNSType _nsType;

    // 事否实现了自定义的映射关系表 
    BOOL _hasCustomWillTransformFromDictionary;
    BOOL _hasCustomTransformFromDictionary;
    BOOL _hasCustomTransformToDictionary;
    BOOL _hasCustomClassFromDictionary;
}
@end
元类meta class

建议阅读: Objective-C 中的元类(meta class)是哪些? 和 唐巧: Objective-C对象模型及使用
轻易易行的说,元类是类对象的类。大家领会,实例对象的秘诀囤积在类中。而类对象的章程,是积累在元类中的,那也使得Object-C的对象模型获得了统一。

@interface NSObject   (nullable instancetype)yy_modelWithJSON:json;  (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;- (nullable id)yy_modelToJSONObject;- (nullable NSData *)yy_modelToJSONData;......

私有类_YYModelMeta

_YYModelMeta 是对 YYClassInfo 的再次卷入

②再看modelSetWithDictionary方法中做吗

1)缓存中得以拿走到moedelMeta
2)提供了二个ModelSetContext

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

3)首先说一下CFArrayApplyFunction那一个方法,词典同理:

void CFArrayApplyFunction(CFArrayRef theArray,
                             CFRange range,
                 CFArrayApplierFunction CF_NOESCAPE applier, 
                                void *context);

在加以的range中遍历theArray,施行applier方法。对应到YYModel的代码,我们看下ModelSetWithDictionaryFunction中做了怎么着职业。

static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
    ModelSetContext *context = _context;
    __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
    __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
    __unsafe_unretained id model = (__bridge id)(context->model);
    while (propertyMeta) {
        if (propertyMeta->_setter) {
            ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
        }
        propertyMeta = propertyMeta->_next;
    };
}

根据propertyMeta遍历,通过ModelSetValueForProperty方法给属性的赋值。ModelSetValueForProperty方法中通过swich-case对两样档案的次序的值一一赋值。最终,大家获得了想要的model!

json与模型的转变框架很多,YYModel 一出,品质吊打同类组件,终于找了些日子目击了一番,确实收益颇多,写下此文作为享受。

YYClassInfo

property: 属性
struct property_t {
    const char *name;
    const char *attributes;
};

源码基于 1.0.4 版本。

初始化

/// Returns the cached model class meta
  (instancetype)metaWithClass:(Class)cls {
    if (!cls) return nil;
    static CFMutableDictionaryRef cache;
    static dispatch_once_t onceToken;
    static dispatch_semaphore_t lock;
    dispatch_once(&onceToken, ^{
        cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        lock = dispatch_semaphore_create(1);
    });
    dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
    dispatch_semaphore_signal(lock);
    if (!meta || meta->_classInfo.needUpdate) {
        meta = [[_YYModelMeta alloc] initWithClass:cls];
        if (meta) {
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
            CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
            dispatch_semaphore_signal(lock);
        }
    }
    return meta;
}

先是从缓存中加载,未有在依照传入cls 创设meta,并做缓存管理, dispatch_semaphore 确定保证线程安全

@implementation _YYModelMeta


- (instancetype)initWithClass:(Class)cls {

    YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
    if (!classInfo) return nil;
    self = [super init];

    // 黑名单 会忽略返回数组里的属性
    NSSet *blacklist = nil;
    if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
        NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
        if (properties) {
            blacklist = [NSSet setWithArray:properties];
        }
    }

    // 白名单 只考虑返回数组内的属性 
    NSSet *whitelist = nil;
    if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
        NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
        if (properties) {
            whitelist = [NSSet setWithArray:properties];
        }
    }

    // 获取容器属性中的映射关系字典
    NSDictionary *genericMapper = nil;
   // 判断类中是否实现了对应的modelContainerPropertyGenericClass方法
    if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {

        genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
      // 存储key和对应的class到字典中
        if (genericMapper) {
            NSMutableDictionary *tmp = [NSMutableDictionary new];
            [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                if (![key isKindOfClass:[NSString class]]) return;
                Class meta = object_getClass(obj);
                if (!meta) return;
                if (class_isMetaClass(meta)) {
                    tmp[key] = obj;
                } else if ([obj isKindOfClass:[NSString class]]) {
                    Class cls = NSClassFromString(obj);
                    if (cls) {
                        tmp[key] = cls;
                    }
                }
            }];
            genericMapper = tmp;
        }
    }

    // 存储所有属性的PropertyMeta对象

    NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
    YYClassInfo *curClassInfo = classInfo;
    while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
        // 遍历当前ClassInfo 中的所有PropertyInfo, 将它们封装成PropertyMeta
        for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
            // 检查是否合法和黑白名单筛选
            if (!propertyInfo.name) continue;
            if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
            if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;

            // 通过propetyInfo来创建PropertyMeta 对象
            _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
                                                                    propertyInfo:propertyInfo
                                                                         generic:genericMapper[propertyInfo.name]];
            // meta name非空
            if (!meta || !meta->_name) continue;
            // 需要实现get方法和set方法
            if (!meta->_getter || !meta->_setter) continue;
            // 字典中已有该字段的meta 避免重复操作
            if (allPropertyMetas[meta->_name]) continue;
            allPropertyMetas[meta->_name] = meta;
        }
        // 遍历父类的property
        curClassInfo = curClassInfo.superClassInfo;
    }

    if (allPropertyMetas.count)
  _allPropertyMetas = allPropertyMetas.allValues.copy;

    // 创建 key :propertyMeta 映射关系字典
    NSMutableDictionary *mapper = [NSMutableDictionary new];
    NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
    NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];

    // 是否实现自定义的映射表
    if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {

        NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];

        // 遍历自定义的字典
        [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {

            _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
            if (!propertyMeta) return;
            // 由于用户自定义映射,把原来映射的数据删除
            [allPropertyMetas removeObjectForKey:propertyName];

            if ([mappedToKey isKindOfClass:[NSString class]]) { 
               // key字段非空
                if (mappedToKey.length == 0) return;
                // 保存映射的key
                propertyMeta->_mappedToKey = mappedToKey;

                // 如果是keyPath的情况处理
                NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];

                if (keyPath.count > 1) {  
                    propertyMeta->_mappedToKeyPath = keyPath;
                    [keyPathPropertyMetas addObject:propertyMeta];
                }

                // 多个属性映射同一个key的时候,用next存储前一个 json Key映射的meta
                propertyMeta->_next = mapper[mappedToKey] ?: nil;
                // 保存新的meta对象
                mapper[mappedToKey] = propertyMeta;

            } else if ([mappedToKey isKindOfClass:[NSArray class]]) { 

         // 一个属性映射多个json Key

                NSMutableArray *mappedToKeyArray = [NSMutableArray new];
                for (NSString *oneKey in ((NSArray *)mappedToKey)) {
                    if (![oneKey isKindOfClass:[NSString class]]) continue;
                    if (oneKey.length == 0) continue;

                    NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
                    if (keyPath.count > 1) {
                        [mappedToKeyArray addObject:keyPath];
                    } else {
                        [mappedToKeyArray addObject:oneKey];
                    }

                    if (!propertyMeta->_mappedToKey) {
                        propertyMeta->_mappedToKey = oneKey;
                        propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
                    }
                }
                if (!propertyMeta->_mappedToKey) return;

                propertyMeta->_mappedToKeyArray = mappedToKeyArray;
                [multiKeysPropertyMetas addObject:propertyMeta];

                propertyMeta->_next = mapper[mappedToKey] ?: nil;
                mapper[mappedToKey] = propertyMeta;
            }
        }];
    }

    // 没有自定义映射规则的属性处理

    [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
        // 直接让mappedKey等于属性名
        propertyMeta->_mappedToKey = name;
        propertyMeta->_next = mapper[name] ?: nil;
        mapper[name] = propertyMeta;
    }];

    // 变量存储
    if (mapper.count) _mapper = mapper;
    if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
    if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;

    _classInfo = classInfo;
    _keyMappedCount = _allPropertyMetas.count;
    _nsType = YYClassGetNSType(cls);
    _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
    _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
    _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
    _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
    return self;
}
method: SEL是接收子,是艺术名的独一标志符。IMP是方式达成的指针。
struct method_t {
    SEL name;
    const char *types;
    IMP imp;

    struct SortBySELAddress :
        public std::binary_function<const method_t&,
                                    const method_t&, bool>
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};

本文由68399皇家赌场发布于最新解决方案,转载请注明出处:YYModel源码分析,YYModel源码学习

关键词: 68399皇家赌场 源码 性能 YYModel iOS分享的demo

最火资讯