> For the complete documentation index, see [llms.txt](https://ryukiedev.gitbook.io/wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ryukiedev.gitbook.io/wiki/framework/codereading-01-yymodel.md).

# 01 CodeReading-01-YYModel

> 感觉值对于优秀源码的学习阅读不是很够,开个坑,希望自己坚持下去 YY作者写的相关文章必看: <https://blog.ibireme.com/2015/10/23/ios_model_framework_benchmark/>

## 结构

加上`.h`只有5个文件相当轻量级的一个转模型框架,十分适合作为源码阅读计划的第一弹

`YYModel.h` `NSObject+YYModel.h` `NSObject+YYModel.m` `YYClassInfo.h` `YYClassInfo.m`

## YYModel.h

头文件

## NSObject+YYModel

> 负责转模型的操作

### 类型

#### YYEncodingNSType

* 对应`Foundation`框架中基本类型的枚举

### 内联函数

> YYClassGetNSType(Class cls)

* 类型映射
* 返回: `YYEncodingNSType`

> YYEncodingTypeIsCNumber(YYEncodingType type)

* 判断是否是`C`的`number`类型
* 返回: `BOOL`

> NSNumber \*YYNSNumberCreateFromID(\_\_unsafe\_unretained id value)

* 从`id`类型返回`NSNumber`
* 其中对空的判断还有小数整数的处理值得一看

> NSDate *YYNSDateFromString(\_\_unsafe\_unretained NSString* string)

* 从字符串获得`NSDate`对象
* 作用域的控制值得学习 `{}`

> Class YYNSBlockClass()

* 获取`NSBlock`类&#x20;
* 在判断`value`类型的时候有用到

> id YYValueForKeyPath(**unsafe\_unretained NSDictionary \*dic,** unsafe\_unretained NSArray \*keyPaths)

* 通过一个 `keyPaths` 来从字典中获取对应 `value`
* 例如 `[@"id",@"ID",@"myId"]` 来从`dic`中获取一个ID

> id YYValueForMultiKeys(**unsafe\_unretained NSDictionary \*dic,** unsafe\_unretained NSArray \*multiKeys)

* Get the value with multi key (or key path) from dictionary
* The dic should be NSDictionary
* 和上一个方法类似但这里的遍历时如果`key`不是`NSString`会调用上面的方法

> NSNumber *ModelCreateNumberFromProperty(**unsafe\_unretained id model,**&#x75;nsafe\_unretained \_YYModelPropertyMeta* meta)

* Get number from property.
* 这里位移枚举的使用值得一看

> void ModelSetNumberToProperty(**unsafe\_unretained id model,**&#x75;nsafe\_unretained NSNumber *num,\_\_unsafe\_unretained \_YYModelPropertyMeta* meta)

* Set number to property.
* 与上面的方法一对

> void ModelSetValueForProperty(**unsafe\_unretained id model,**&#x75;nsafe\_unretained id value,\_\_unsafe\_unretained \_YYModelPropertyMeta \*meta)

* Set value to model with a property meta.
* 核心函数之一

> void ModelSetWithDictionaryFunction(const void *\_key, const void* \_value, void \*\_context)

* Apply function for dictionary, to set the key-value pair to model.
* 在`void CFDictionaryApplyFunction(CFDictionaryRef theDict, CFDictionaryApplierFunction applier, void *context);`方法中调用
* 对字典中所有键值对执行`applier`函数

> void ModelSetWithPropertyMetaArrayFunction(const void *\_propertyMeta, void* \_context)

* Apply function for model property meta, to set dictionary to model.
* 和上面的类似,是数组的

### Class

#### 结构体 ModelSetContext

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

#### \_YYModelPropertyMeta : NSObject

* A property info in object model.
* 表示模型对象中的属性信息
* 对应`YYClassPropertyInfo`
* 可理解为 属性 中间件

#### \_YYModelMeta : NSObject

* A class info in object model.
* 表示模型的类信息
* 对应`YYClassInfo`
* 可理解为 类 中间件

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

* 其中使用了 `dispatch_semaphore`信号量保证线程安全
* 建立了一个静态缓存,缓存了每个`class`对应的`_YYModelMeta`对象,提高了性能

## YYClassInfo

> 将`Runtime`中`C`的一些东西,通过面向对象的方式进行了封装,便于理解使用 转模型的基础

### 类型

#### YYEncodingType

> Type encoding's type.

* 列举了各类编码信息，包括值类型、方法限定类型、属性修饰类型。YYEncodingType使用掩码的方式对三类不同的枚举信息进行分类，各占据1个字节

```swift
///值类型
YYEncodingTypeMask                = 0xFF       
///方法限定类型
YYEncodingTypeQualifierMask      = 0xFF00       
///属性修饰类型
YYEncodingTypePropertyMask      = 0xFF0000
```

### Class

#### YYClassIvarInfo

> 成员变量 objc\_ivar

#### YYClassMethodInfo

> 方法 objc\_method

#### YYClassPropertyInfo

> 成员属性 property\_t

#### YYClassInfo

> 类 objc\_class

```swift
/.../

/// 成员变量列表
@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; ///<

/.../
```

## Tips:

### kCFNull

```swift
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
    if (!json || json == (id)kCFNull) return nil;
    ...
}
```

* 点进去

```swift
/* Null representant */

typedef const struct CF_BRIDGED_TYPE(NSNull) __CFNull * CFNullRef;

CF_EXPORT
CFTypeID CFNullGetTypeID(void);

CF_EXPORT
const CFNullRef kCFNull;    // the singleton null instance
```

* 打印

```swift
id null1 = (id)kCFNull;
id null2 = [NSNull null];
```

* 打印地址

```swift
null1=(NSNull *)0x10426eaf0
null2=(NSNull *)0x10426eaf0
```

* 拓展

| 标志     | 值              | 含义                 |
| ------ | -------------- | ------------------ |
| NULL   | (void \*)0     | C指针的字面零值           |
| nil    | (id)0          | Objective-C对象的字面零值 |
| Nil    | (Class)0       | Objective-C类的字面零值  |
| NSNull | \[NSNull null] | 用来表示零值的单独的对象       |

### static inline 内联函数

* 看到这样一个宏 `#define force_inline __inline__ __attribute__((always_inline))`
* 挺好的一篇参考文章: <http://www.cnblogs.com/wyk19910103/p/5660627.html>

### \_\_unsafe\_unretained

* 与`weak`类似但有本质不同
* <https://www.zhihu.com/question/55831650>
* <https://blog.csdn.net/bsplover/article/details/7707964>
* <https://blog.csdn.net/yueyansheng2/article/details/49813987>

### \_\_bridge

`OC`与`C`指针的转换

### 命名对内对外

* 对内的加`_`前缀
* 对外的不加
* `CoreFoundation`中也挺常见

### CFDictionaryApplyFunction & CFDictionaryApplyFunction 函数

* 相对于 `Foundation` 的方法来说，`CoreFoundation` 的方法有更高的性能，用 `CFArrayApplyFunction()` 和 `CFDictionaryApplyFunction()` 方法来遍历容器类能带来不少性能提升，但代码写起来会非常麻烦。

### 性能优化Tips:

* 来自文章开头YY作者的文章,感觉相当重要这里`Copy`来方便看

```swift
1. 缓存
Model JSON 转换过程中需要很多类的元数据，如果数据足够小，则全部缓存到内存中。

2. 查表
当遇到多项选择的条件时，要尽量使用查表法实现，比如 switch/case，C Array，如果查表条件是对象，则可以用 NSDictionary 来实现。

3. 避免 KVC
Key-Value Coding 使用起来非常方便，但性能上要差于直接调用 Getter/Setter，所以如果能避免 KVC 而用 Getter/Setter 代替，性能会有较大提升。

4. 避免 Getter/Setter 调用
如果能直接访问 ivar，则尽量使用 ivar 而不要使用 Getter/Setter 这样也能节省一部分开销。

5. 避免多余的内存管理方法
在 ARC 条件下，默认声明的对象是 __strong 类型的，赋值时有可能会产生 retain/release 调用，如果一个变量在其生命周期内不会被释放，则使用 __unsafe_unretained 会节省很大的开销。

访问具有 __weak 属性的变量时，实际上会调用 objc_loadWeak() 和 objc_storeWeak() 来完成，这也会带来很大的开销，所以要避免使用 __weak 属性。
创建和使用对象时，要尽量避免对象进入 autoreleasepool，以避免额外的资源开销。

6. 遍历容器类时，选择更高效的方法
相对于 Foundation 的方法来说，CoreFoundation 的方法有更高的性能，用 CFArrayApplyFunction() 和 CFDictionaryApplyFunction() 方法来遍历容器类能带来不少性能提升，但代码写起来会非常麻烦。

7. 尽量用纯 C 函数、内联函数
使用纯 C 函数可以避免 ObjC 的消息发送带来的开销。如果 C 函数比较小，使用 inline 可以避免一部分压栈弹栈等函数调用的开销。

8. 减少遍历的循环次数
在 JSON 和 Model 转换前，Model 的属性个数和 JSON 的属性个数都是已知的，这时选择数量较少的那一方进行遍历，会节省很多时间。
```

## 参考

* <http://triplecc.github.io/blog/2016-06-05-yymodelyue-du-xiao-ji/>
* <https://lision.me/yymodel_x01/>
* <https://lision.me/yymodel_x02/>
