感觉值对于优秀源码的学习阅读不是很够,开个坑,希望自己坚持下去 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类 - 在判断
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,__unsafe_unretained _YYModelPropertyMeta *meta)
- Get number from property.
- 这里位移枚举的使用值得一看
void ModelSetNumberToProperty(__unsafe_unretained id model,__unsafe_unretained NSNumber *num,__unsafe_unretained _YYModelPropertyMeta *meta)
- Set number to property.
- 与上面的方法一对
void ModelSetValueForProperty(__unsafe_unretained id model,__unsafe_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
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 - 可理解为 类 中间件
/// 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个字节
///值类型YYEncodingTypeMask = 0xFF///方法限定类型YYEncodingTypeQualifierMask = 0xFF00///属性修饰类型YYEncodingTypePropertyMask = 0xFF0000Class
YYClassIvarInfo
成员变量 objc_ivar
YYClassMethodInfo
方法 objc_method
YYClassPropertyInfo
成员属性 property_t
YYClassInfo
类 objc_class
/.../
/// 成员变量列表@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
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json { if (!json || json == (id)kCFNull) return nil; ...}- 点进去
/* Null representant */
typedef const struct CF_BRIDGED_TYPE(NSNull) __CFNull * CFNullRef;
CF_EXPORTCFTypeID CFNullGetTypeID(void);
CF_EXPORTconst CFNullRef kCFNull; // the singleton null instance- 打印
id null1 = (id)kCFNull;id null2 = [NSNull null];- 打印地址
null1=(NSNull *)0x10426eaf0null2=(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
__bridge
OC与C指针的转换
命名对内对外
- 对内的加
_前缀 - 对外的不加
CoreFoundation中也挺常见
CFDictionaryApplyFunction & CFDictionaryApplyFunction 函数
- 相对于
Foundation的方法来说,CoreFoundation的方法有更高的性能,用CFArrayApplyFunction()和CFDictionaryApplyFunction()方法来遍历容器类能带来不少性能提升,但代码写起来会非常麻烦。
性能优化Tips:
- 来自文章开头YY作者的文章,感觉相当重要这里
Copy来方便看
1. 缓存Model JSON 转换过程中需要很多类的元数据,如果数据足够小,则全部缓存到内存中。
2. 查表当遇到多项选择的条件时,要尽量使用查表法实现,比如 switch/case,C Array,如果查表条件是对象,则可以用 NSDictionary 来实现。
3. 避免 KVCKey-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 的属性个数都是已知的,这时选择数量较少的那一方进行遍历,会节省很多时间。