详见代码如下,YYModel的目录结构很简单

  1. 运营商音讯:
    直接运用CTTelephonyNetworkInfo的劳动即可获得运营商的名字,
    运营商编码等音讯. easy!

前言

YYModel 是一个iOS
JSON模型转化库,和此外部分同类型库相比较,具有相比较好的特性优势。本文少禽对YYModel的源码进行分析,具体用法小编ibireme在github中有提及。YYModel的目录结构很简短,唯有三个类,
NSObject+YYModelYYClassInfoYYClassInfo重大对根类NSObject 的
Ivar , Method,
Property以及Class自己举办了打包,NSObject+YYModel
NSObject的归类,扩大了一部分JSON模型转化的主意。下图一张YYmodel的总体布局图:

图片 1

YYModel 结构图.png

YYClassInfo

-(NSString *)currentCarrierName{
    CTTelephonyNetworkInfo *telephonyInfo = [[CTTelephonyNetworkInfo alloc] init];
    CTCarrier *carrier = [telephonyInfo subscriberCellularProvider];
    return [[NSString alloc] initWithFormat:@"%@_%@_%@_%@", [carrier carrierName], [carrier mobileCountryCode], [carrier mobileNetworkCode], [carrier isoCountryCode]];
}

要点

YYClassIvarInfo : 对 Class的Ivar进行了打包
YYClassMethodInfo : 对 Class的Method举行了打包
YYClassPropertyInfo : 对 Class的Property进行了打包
YYClassInfo :
对Class举办了包装,包涵了YYClassIvarInfo,YYClassMethodInfo,YYClassPropertyInfo

  1. 现阶段互联网项目(WiFi/4G) 信号强弱
    谷歌到的一个稿子, 大概是造访statusBar的视图,
    然后KVC访问该视图的品质, 从而得到想要的新闻.
    小说没有关系如何赢得信号强度, 可是, 顺着它的思绪,
    很快就找到了关键点:

变量类型编码

说到变量,可能大家写代码中最广大的就是变量了。当大家自定义一个类时,首先总是会申美赞臣(Meadjohnson)些质量,而且每个属性都会含有一些修饰词。比如是或不是原子性,内存管理规范,只读性。这个都可以经过那几个特性的property_getAttributes
方法取得,苹果为保有的门类,包蕴属性类型都有编码,具体可以查阅苹果官方文档:类型编码,
苹果官方文档:属性类型
上面是一个容易易行例子:

  1. 发明属性

@interface Cat : NSObject
@end

@interface Person : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) NSInteger age;
@property (nonatomic,strong) Cat *cat;
- (NSDictionary *)getProperties;
@end

2.透过runtime 来博取具有属性

#import "Person.h"
#import <objc/runtime.h>
@implementation Person
- (NSDictionary *) getProperties{
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (NSUInteger  i = 0; i < count; i++) {
        const char *name = property_getName(propertyList[i]);
        const char *attribute = property_getAttributes(propertyList[i]);
        NSString *nameStr = [NSString stringWithUTF8String:name];
        NSString *attributeStr = [NSString stringWithUTF8String:attribute];
        dic[nameStr] = attributeStr;
    }
    return [dic copy];
}

@end

3.打印结果

2017-01-03 10:54:34.690 Properties[16941:881462] {
    age = "Tq,N,V_age";
    cat = "T@\"Cat\",&,N,V_cat";
    name = "T@\"NSString\",C,N,V_name";
}

俺们得以看到属性的具有类型编码新闻,其中第四个象征是其一变量的品类,以T开首,最终一个意味着的是变量的名字,一般用V_属性名表示,中间的一部分就是大家注解的修饰符。比如age的花色是
Tq,而在官方文档中q
代表了A long long,64bit下NSInteger的取值范围就是long == long long
,N代表了非原子性,变量名是_age。其他的@代表了OC类型 id
,cat类型即是T@”Cat”,&代表了 那些变量是retain
(ARC下strong相当于retain),C 代表了copy

YYEncodingType

根据项目编码自定义了品种枚举,包蕴了多个部分

YYEncodingTypeMask : 0~8位的值,变量的数据类型
YYEncodingTypeQualifierMask : 8~16位的值,变量的形式类型
YYEncodingTypePropertyMask: 16~24位的值,变量的特性类型

此处把枚举值分成八个部分,通过 枚举值 & 对应 Mask
取出对应的变量类型,区分分裂门类部分。YYEncodingGetType
是按照变量的数据类型编码值获取自定义YYEncodingType

类:**UIStatusBarDataNetworkItemView**, **UIStatusBarSignalStrengthItemView**
属性:**dataNetworkType**, **wifiStrengthBars**, **signalStrengthBars**

Var Method Property

YYClassIvarInfo:用于存取变量的音信

/**
 Instance variable information.
 */
@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是代表实例变量的品种,其实际是一个指向objc_ivar结构体的指针,其定义如下:

struct objc_ivar {  
    charchar *ivar_name                              OBJC2_UNAVAILABLE;  
    charchar *ivar_type                              OBJC2_UNAVAILABLE;  
    int ivar_offset                                  OBJC2_UNAVAILABLE;  
#ifdef __LP64__  
    int space                                        OBJC2_UNAVAILABLE;  
#endif  
}  

运用runtime,name通过的ivar_getName获取,offset
通过ivar_getOffset获取,typeEncoding 通过 ivar_getTypeEncoding
获取,type 通过自定义方法
YYEncodingGetType获取。其中offset是变量的基地址偏移量,可以透过它来直接访问变量数据,上边是例证:

    Person *p = [[Person alloc]init];
//    NSLog(@"%@",[p getProperties]);
    p.age = 20;
    Ivar age_ivar = class_getInstanceVariable([Person class], "_age");
    long *age_pointer = (long *)((__bridge void  *)(p) + ivar_getOffset(age_ivar));
    NSLog(@"age ivar offset = %td", ivar_getOffset(age_ivar));
    *age_pointer = 10;
    NSLog(@"%@", p);

- (NSString *)description {
    NSLog(@"current pointer = %p", self);
    NSLog(@"age pointer = %p", &_age);
    return [NSString stringWithFormat:@"age = %zi", _age];
}

打印结果:

2017-01-03 14:41:14.175 Properties[20934:1554728] age ivar offset = 16
2017-01-03 14:41:14.176 Properties[20934:1554728] current pointer = 0x600000075140
2017-01-03 14:41:14.176 Properties[20934:1554728] age pointer = 0x600000075150
2017-01-03 14:41:14.177 Properties[20934:1554728] age = 10

YYClassMethodInfo:用于存取方法的音信

/**
 Method information.
 */
@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

Method 的音信同 Ivar 一样,通过runtime的
method相关方法得到,其余的一对信息同Ivar,首要来说一下 SELImp

SEL: 方法ID,C字符串

typedef struct objc_selector *SEL;

/// Defines a method
struct objc_method_description {
    SEL name;               /**< The name of the method */
    char *types;            /**< The types of the method arguments */
};

IMP:方法函数指针

OC是动态语言,方法调用(也号称新闻发送)是在运行时动态绑定的,而非编译时。怎么着完结正确的调用指定的措施吧?那里就需要用到SEL和IMP。编译器会将音讯发送转换成对objc_msgSend(void /* id self, SEL op, ... */ )情势的调用
objc_msgSend主意依照目的的isa指针找到对象的类,通过在类中的调度表(dispatch
table)中检索SEL 得到 IMP,精确执行指定方法。

YYClassPropertyInfo: 用于存取属性的音讯

/**
 Property information.
 */
@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

YYClassInfo: 对Class的包裹,包罗了地点三有些的新闻

/**
 Class information for a class.
 */
@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

详见代码如下:

元类

其中有metaCls意味着了元类。那怎么着是元类呢?上面是一张经典的类结构图

图片 2

isa.png

在OC中,每个实例对象都有一个isa指针,它指向了目标的class。而那些class也一致有一个isa指针,它就是指向了它的元类,其实类也是一个对象,所以目的之于类的关联,就一定于类(类对象)之于其元类(类对象的类)的关系。那元类有什么用啊?大家都了然在OC中调用方法有实例方法和类方式。大家调用实例方法,就是经过isa指针找到指定的class,查找存储在class中的方法列表执行形式,所以元类的功效就是调用类方法时,通过寻找保存在元类中的类方式执行措施的作用。那为啥不把所有办法都保存在类中,可能这么越发速速也节约资源吧,具体可以协调招来资料。

在YYClassInfo中,有一个_update措施,用来更新类中存储的信息。

-(NSString *)currentNetworkType{
    NSArray *subviews = [[[[UIApplication sharedApplication] valueForKey:@"statusBar"] valueForKey:@"foregroundView"]subviews];

    NSNumber *dataNetworkItemView = nil;
    NSNumber *signalStrengthItemView = nil;
    for (id subview in subviews) {
        if([subview isKindOfClass:[NSClassFromString(@"UIStatusBarDataNetworkItemView") class]]) {
            dataNetworkItemView = subview;
        }
        if ([subview isKindOfClass:[NSClassFromString(@"UIStatusBarSignalStrengthItemView") class]]) {
            signalStrengthItemView = subview;
        }
        if (dataNetworkItemView && signalStrengthItemView) {
            break;
        }
    }

    if (!dataNetworkItemView) {
        return nil;
    }

    NSNumber *networkTypeNum = [dataNetworkItemView valueForKey:@"dataNetworkType"];
    NSNumber *wifiStrengthBars = [dataNetworkItemView valueForKey:@"wifiStrengthBars"];
    NSNumber *signalStrengthBars = [signalStrengthItemView valueForKey:@"signalStrengthBars"];
    if (!networkTypeNum) {
        return nil;
    }

    NSInteger networkType = [networkTypeNum integerValue];
    switch (networkType) {
        case 0:
            return @"No Service";
            break;

        case 1:
            return [[NSString alloc] initWithFormat:@"%@_%@", @"2G", signalStrengthBars];
            break;

        case 2:
            return [[NSString alloc] initWithFormat:@"%@_%@", @"3G", signalStrengthBars];
            break;

        case 3:
            return [[NSString alloc] initWithFormat:@"%@_%@", @"4G", signalStrengthBars];
            break;

        case 4:
            return [[NSString alloc] initWithFormat:@"%@_%@", @"LTE", signalStrengthBars];
            break;

        case 5:
            return [[NSString alloc] initWithFormat:@"%@_%@", @"WiFi", wifiStrengthBars];
            break;
        default:
            return [[NSString alloc] initWithFormat:@"%@_%@_%@", networkTypeNum, wifiStrengthBars, signalStrengthBars];
            break;
    }
}

开首化方法

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

要点:

  • CFDictionaryCreateMutable 和 CFDictionarySetValue
    不是线程安全的,所以要求创设锁,选取 dispatch_semaphore
    通过控制信号量落成了锁

  • 设置缓存,假如在缓存中设有class,则直接得到到相应的ivar,method,property,否者创造YYClassInfo实例对象

  1. 授之以渔
    自身是怎么领悟地方说的这一个类有哪些可以访问的属性的吧?
    答案是依靠一个卓绝的开源库YYModel.
    理所当然, 自己用OC的runtime方法写也是可以的,
    只是没有用YYModel已经封装好的点子方便.

NSObject+YYModel:

NSObject+YYModel是YYModel的主干类,紧要部分:

强制内联C函数:功效函数
私有类_YYModelPropertyMeta : 管理Model属性的多少, 类型,
映射的key,keyPath
私有类 _YYModelMeta :管理Model 数据,类型,存储
映射key,keypath,_YYModelPropertyMeta
NSObject NSArray NSDictionary (YYModel) :
多少个分类,YYModel主体成效落成
YYModel 协议:增加作用达成

私有类_YYModelPropertyMeta

_YYModelPropertyMeta是对地点的 YYClassPropertyInfo 的进一步封装。

#import "YYModel.h"
//...
    YYClassInfo *info = [YYClassInfo classInfoWithClass:[signalStrengthItemView class]];
    NSLog(@"dataNetworkItemView ivars:%@", info.ivarInfos);
    NSLog(@"dataNetworkItemView properties:%@", info.propertyInfos);
    NSLog(@"dataNetworkItemView method:%@", info.methodInfos);

个中实例变量

/// A property info in object model.
// model property的进一步分装
@interface _YYModelPropertyMeta : NSObject {
    @package
    NSString *_name;              //属性名
    YYEncodingType _type;         //属性的编码类型
    YYEncodingNSType _nsType;     //属性的Foundation类型
    BOOL _isCNumber;              //是否c语言的数字
    Class _cls;                   //属性的class
    Class _genericCls;    //属性内包含的类class
    SEL _getter;             //属性 getter方法
    SEL _setter;             //属性 setter方法
    BOOL _isKVCCompatible;     //是否可以使用KVC
    BOOL _isStructAvailableForKeyedArchiver;   //是否struct并且可以归档
    BOOL _hasCustomClassFromDictionary;   //是否包含本本地的class转换
    /*
     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;     //属性名映射的 key 
    NSArray *_mappedToKeyPath;  //属性名映射的 keyPath 
    NSArray *_mappedToKeyArray;  //属性名的映射的key keyPath 数组 
    YYClassPropertyInfo *_info;  //属性的YYClassPropertyInfo info
    _YYModelPropertyMeta *_next;  
    //多个属性名映射到同一个key 时,指向下一个属性名的YYModelPropertyMeta 指针
}
@end

重如若最后多少个变量。其中_mappedToKey _mappedToKeyPath
_mappedToKeyArray 是性质映射的key,keyPath ,key (keypath)
数组_mappedToKeyArray 中得以是key和keyPath,实际取其中首个映射到的值

貌似一个属性名对应一个key值,即使七个属性名对应同一个key,那里就必要next发挥效用了。比如

{
    @"name1" : @"name",
    @"name2" : @"name",
    @"name3" : @"name",
}

第一name1开始获得映射,对mapKey举办赋值,取得json中的name字段进行赋值一密密麻麻操作,此时next指针为nil
name2接着举行映射,对mapKey进行赋值,接着取得原来json
key对应的特性描述对象,将name2的next指针,指向name1。
name3接着实行映射,对mapKey举办赋值,接着取得原来json
key对应的属性描述对象,将name3的next指针,指向name2。

代码中的完毕

    propertyMeta->_next = mapper[mappedToKey] ?: nil;
    mapper[mappedToKey] = propertyMeta;
  1. 结束:)

伊始化方法

@implementation _YYModelPropertyMeta
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
    //创建并且根据propertyInfo 进行变量赋值
    _YYModelPropertyMeta *meta = [self new];
    meta->_name = propertyInfo.name;
    meta->_type = propertyInfo.type;
    meta->_info = propertyInfo;

    // 属性为容器类型的时候, 映射类型赋值
    meta->_genericCls = generic;

    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) { 
         // 是否Foundation 类型
        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:
         */
        static NSSet *types = nil;
        static dispatch_once_t onceToken;
        // 单例 创建C结构体类型映射
        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}"];
            // 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;
        }
    }
    // 设置class类型
    meta->_cls = propertyInfo.cls;

    // 如果是容器类型
    if (generic) {
        // 从容器class 中读取
        meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
    } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
        // 从class类型中读取
        meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
    }

    // 设置 getter 和 setter 方法
    if (propertyInfo.getter) {
        if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
            meta->_getter = propertyInfo.getter;
        }
    }
    if (propertyInfo.setter) {
        if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
            meta->_setter = propertyInfo.setter;
        }
    }

    /**
     *  只有实现了getter和setter方法 才能实现归档
     */
    if (meta->_getter && meta->_setter) {
        /*
        类型是否支持 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;
}
@end

私有类_YYModelMeta

_YYModelMeta 是对 YYClassInfo 的重复卷入

其间变量

@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

初始化

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

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)

Model 转 JSON

其间有效的JSON Object 只可以是以下连串:
NSArray,NSDictionary,NSString,NSNumber,NSNull。

1.假设是NSDictionary,NSSet,NSArray 类型,递归调用此办法得到JSON
Object
2.如果是NSURL,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;
}

参考资料

郑钦洪_:YYModel
源码历险记

相关文章