Comment on page
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
头文件
负责转模型的操作
- 对应
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.
- 和上面的类似,是数组的
typedef struct {
void *modelMeta; ///< _YYModelMeta
void *model; ///< id (self)
void *dictionary; ///< NSDictionary (json)
} ModelSetContext;
- A property info in object model.
- 表示模型对象中的属性信息
- 对应
YYClassPropertyInfo
- 可理解为 属性 中间件
- 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
对象,提高了性能
将Runtime
中C
的一些东西,通过面向对象的方式进行了封装,便于理解使用 转模型的基础
Type encoding's type.
- 列举了各类编码信息,包括值类型、方法限定类型、属性修饰类型。YYEncodingType使用掩码的方式对三类不同的枚举信息进行分类,各占据1个字节
///值类型
YYEncodingTypeMask = 0xFF
///方法限定类型
YYEncodingTypeQualifierMask = 0xFF00
///属性修饰类型
YYEncodingTypePropertyMask = 0xFF0000
成员变量 objc_ivar
方法 objc_method
成员属性 property_t
类 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; ///<
/.../
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
if (!json || json == (id)kCFNull) return nil;
...
}
- 点进去
/* 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
- 打印
id null1 = (id)kCFNull;
id null2 = [NSNull null];
- 打印地址
null1=(NSNull *)0x10426eaf0
null2=(NSNull *)0x10426eaf0
- 拓展
标志 | 值 | 含义 |
NULL | (void *)0 | C指针的字面零值 |
nil | (id)0 | Objective-C对象的字面零值 |
Nil | (Class)0 | Objective-C类的字面零值 |
NSNull | [NSNull null] | 用来表示零值的单独的对象 |
- 看到这样一个宏
#define force_inline __inline__ __attribute__((always_inline))
OC
与C
指针的转换- 对内的加
_
前缀 - 对外的不加
CoreFoundation
中也挺常见
- 相对于
Foundation
的方法来说,CoreFoundation
的方法有更高的性能,用CFArrayApplyFunction()
和CFDictionaryApplyFunction()
方法来遍历容器类能带来不少性能提升,但代码写起来会非常麻烦。
- 来自文章开头YY作者的文章,感觉相当重要这里
Copy
来方便看
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 的属性个数都是已知的,这时选择数量较少的那一方进行遍历,会节省很多时间。
Last modified 2yr ago