YYModel源码学习
YYModel的代码结构

YYClassInfo

YYClassInfo 主要将 Runtime 层级的一些结构体封装到 NSObject 层级以便调用,以下为对应内容
| YYClassInfo | Runtime |
|---|---|
| YYClassIvarInfo | objc_ivar |
| YYClassMethodInfo | objc_method |
| YYClassPropertyInfo | property_t |
| YYClassInfo | objc_class |
YYEncodingType 枚举
// 定义枚举类型,使用NS_OPTIONS宏表示按位掩码
typedef NS_OPTIONS(NSUInteger, YYEncodingType) {
/* 基础类型掩码和枚举值 (0-255) */
YYEncodingTypeMask = 0xFF, ///< 基础类型掩码(用于通过按位与操作提取基础类型)
YYEncodingTypeUnknown = 0, ///< 未知类型
YYEncodingTypeVoid = 1, ///< void类型
YYEncodingTypeBool = 2, ///< bool类型(包括C++ bool和C99 _Bool)
YYEncodingTypeInt8 = 3, ///< 8位有符号整数(char / BOOL)
YYEncodingTypeUInt8 = 4, ///< 8位无符号整数(unsigned char)
YYEncodingTypeInt16 = 5, ///< 16位有符号整数(short)
YYEncodingTypeUInt16 = 6, ///< 16位无符号整数(unsigned short)
YYEncodingTypeInt32 = 7, ///< 32位有符号整数(int)
YYEncodingTypeUInt32 = 8, ///< 32位无符号整数(unsigned int)
YYEncodingTypeInt64 = 9, ///< 64位有符号整数(long long)
YYEncodingTypeUInt64 = 10, ///< 64位无符号整数(unsigned long long)
YYEncodingTypeFloat = 11, ///< 单精度浮点数(float)
YYEncodingTypeDouble = 12, ///< 双精度浮点数(double)
YYEncodingTypeLongDouble = 13, ///< 扩展双精度浮点数(long double)
YYEncodingTypeObject = 14, ///< 对象类型(id、NSObject *等)
YYEncodingTypeClass = 15, ///< 类类型(Class)
YYEncodingTypeSEL = 16, ///< 选择器类型(SEL)
YYEncodingTypeBlock = 17, ///< 块类型(block)
YYEncodingTypePointer = 18, ///< 指针类型(void*)
YYEncodingTypeStruct = 19, ///< 结构体类型(struct)
YYEncodingTypeUnion = 20, ///< 联合体类型(union)
YYEncodingTypeCString = 21, ///< C字符串(char *)
YYEncodingTypeCArray = 22, ///< C数组(char[10]等)
/* 类型限定符掩码和枚举值 (256-32768) */
YYEncodingTypeQualifierMask = 0xFF00, ///< 类型限定符掩码(用于提取修饰符)
YYEncodingTypeQualifierConst = 1 << 8, ///< const限定符(const int)
YYEncodingTypeQualifierIn = 1 << 9, ///< in限定符(用于函数参数)
YYEncodingTypeQualifierInout = 1 << 10, ///< inout限定符(用于函数参数)
YYEncodingTypeQualifierOut = 1 << 11, ///< out限定符(用于函数参数)
YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy限定符(ARC属性)
YYEncodingTypeQualifierByref = 1 << 13, ///< byref限定符(__block变量)
YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway限定符(异步访问)
/* 属性特性掩码和枚举值 (65536-16777216) */
YYEncodingTypePropertyMask = 0xFF0000, ///< 属性特性掩码(用于提取属性关键字)
YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly属性(只读)
YYEncodingTypePropertyCopy = 1 << 17, ///< copy属性(内存管理语义)
YYEncodingTypePropertyRetain = 1 << 18, ///< retain属性(MRC内存管理)
YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic属性(非原子性)
YYEncodingTypePropertyWeak = 1 << 20, ///< weak属性(弱引用)
YYEncodingTypePropertyCustomGetter = 1 << 21, ///< 自定义getter方法(getter=methodName)
YYEncodingTypePropertyCustomSetter = 1 << 22, ///< 自定义setter方法(setter=methodName)
YYEncodingTypePropertyDynamic = 1 << 23, ///< dynamic属性(动态合成,@dynamic)
};
YYClassIvarInfo
- YYClassIvarInfo:
/**
* 实例变量信息类
*
* 这个类封装了 Objective-C 实例变量(ivar)的元数据信息,用于在运行时获取和操作实例变量的属性。
* 它是 YYModel 框架中类型编码和元数据系统的核心组成部分。
*/
@interface YYClassIvarInfo : NSObject
/**
* ivar 不透明结构体
*
* 这是底层的 Objective-C Ivar 结构,包含实例变量的原始信息。
* 注意:这个指针是只读的,不应直接修改其内容。
*/
@property (nonatomic, assign, readonly) Ivar ivar;
/**
* Ivar 的名称
*
* 实例变量的名称字符串,如 "_username", "_age" 等。
* 这是从 ivar 结构中提取的只读属性。
*/
@property (nonatomic, strong, readonly) NSString *name;
/**
* Ivar 的偏移量
*
* 表示该实例变量在对象内存布局中的字节偏移量。
* 使用 ptrdiff_t 类型确保在32位和64位系统上的正确性。
*
* 示例:在以下结构体中:
* struct {
* id isa; // 偏移 0
* NSString *_name; // 偏移 8 (64位系统)
* }
*/
@property (nonatomic, assign, readonly) ptrdiff_t offset;
/**
* Ivar 的类型编码字符串
*
* 表示实例变量类型的 Objective-C 类型编码字符串。
*
* 示例:
* NSString → @"NSString"
* int → "i"
* float → "f"
* id → "@"
* SEL → ":"
*/
@property (nonatomic, strong, readonly) NSString *typeEncoding;
//不多做赘述
@property (nonatomic, assign, readonly) YYEncodingType type;
- (instancetype)initWithIvar:(Ivar)ivar;
@end
objc_ivar:
struct objc_ivar {
char * _Nullable ivar_name OBJC2_UNAVAILABLE; // 变量名称
char * _Nullable ivar_type OBJC2_UNAVAILABLE; // 变量类型
int ivar_offset OBJC2_UNAVAILABLE; // 变量偏移量
#ifdef __LP64__ // 如果已定义 __LP64__ 则表示正在构建 64 位目标
int space OBJC2_UNAVAILABLE; // 变量空间
#endif
}
YYClassMethodInfo
/**
* 类方法信息封装类
*
* 这个类封装了 Objective-C 方法的元数据信息,提供对方法结构及其类型签名的便捷访问。
* 它是 YYModel 框架中方法元数据系统的核心组件,用于动态方法调用和方法签名解析。
*/
@interface YYClassMethodInfo : NSObject
/**
* 方法不透明结构体
*
* 底层 Objective-C Method 结构,包含方法签名、实现等信息。
* 这是只读属性,避免直接操作底层结构。
*/
@property (nonatomic, assign, readonly) Method method;
/**
* 方法名称
*
* 方法名的字符串表示(例如 "viewDidLoad", "setName:" 等)。
* 格式为 Objective-C 标准的消息签名格式。
*/
@property (nonatomic, strong, readonly) NSString *name;
/**
* 方法选择器
*
* SEL 类型值,用于消息传递系统。
* 与 name 属性不同的是,SEL 是运行时唯一标识符,
* 可以更高效地进行方法比较和调用。
*/
@property (nonatomic, assign, readonly) SEL sel;
/**
* 方法实现(IMP)
*
* 指向方法实际实现的函数指针。
* 通过 imp 可以直接调用方法,绕过运行时消息转发机制,
* 提供最高效的方法调用方式(但需正确处理参数传递)。
*/
@property (nonatomic, assign, readonly) IMP imp;
/**
* 完整类型编码字符串
*
* 描述方法的返回值类型和所有参数类型的完整编码字符串。
*
* 格式示例:
* - (void)method; → "v16@0:8"
* - (BOOL)setValue:(id)forKey:(NSString *) → "B32@0:8@16@24"
*
* 编码规则:
* - 第1部分:返回值类型(如 v 表示 void)
* - 第2部分:方法接受者类型(@)
* - 第3部分:方法选择器类型(:)
* - 后续部分:方法参数类型(从0开始)
*/
@property (nonatomic, strong, readonly) NSString *typeEncoding;
/**
* 返回值类型编码
*
* 方法返回值的类型编码字符串。
* 直接截取自 typeEncoding 的第一部分。
*
* 示例:
* - (NSString *) → "@"
* - (BOOL) → "B"
* - (void) → "v"
*/
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding;
/**
* 参数类型编码数组
*
* 包含所有参数类型编码的有序数组(包括隐式的 self 和 _cmd)。
* 数组顺序与方法签名顺序一致:
* 索引0: 方法接受者 (self) → 类型固定为 "@"
* 索引1: 方法选择器 (_cmd) → 类型固定为 ":"
* 索引2+: 自定义参数
*
* 示例方法:- (void)setValue:(id)value forKey:(NSString *)key
* returnTypeEncoding: "v"
* argumentTypeEncodings: ["@", ":", "@", "@"]
*/
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings;
/**
* 创建并返回方法信息对象
*
* 这是核心初始化方法,用于封装 Method 结构体并提取所有相关信息。
*
* @param method 方法的不透明结构体(Method 指针)
*
* 初始化流程:
* 1. 提取方法名(name)和选择器(sel)
* 2. 获取方法实现(IMP)
* 3. 解析完整类型编码(typeEncoding)
* 4. 分割出返回类型和参数类型编码
* 5. 验证编码格式的正确性
*
* @return 一个新的 YYClassMethodInfo 对象,如果输入无效则为 nil
*/
- (instancetype)initWithMethod:(Method)method;
@end
- objc_method
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE; // 方法名称
char * _Nullable method_types OBJC2_UNAVAILABLE; // 方法类型
IMP _Nonnull method_imp OBJC2_UNAVAILABLE; // 方法实现(函数指针)
}
YYClassPropertyInfo
- YYClassPropertyInfo
/**
* 属性信息封装类
*/
@interface YYClassPropertyInfo : NSObject
// 底层属性结构体指针(指向 Objective-C 的 objc_property_t 结构)
@property (nonatomic, assign, readonly) objc_property_t property;
// 属性名称(如 "username", "age" 等)
@property (nonatomic, strong, readonly) NSString *name;
// 属性类型集合(YYEncodingType 位掩码,包含类型/限定符/特性)
@property (nonatomic, assign, readonly) YYEncodingType type;
// 属性类型编码字符串(如 "@\"NSString\"", "i", "f" 等)
@property (nonatomic, strong, readonly) NSString *typeEncoding;
// 关联的实例变量名称(如 "_username")
@property (nonatomic, strong, readonly) NSString *ivarName;
// 属性对应的类(仅当属性是对象类型时有效)
@property (nullable, nonatomic, assign, readonly) Class cls;
// 属性遵守的协议列表(如 @[@"NSCopying"],无协议则为 nil)
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols;
// Getter 方法的选择器(非空,可能是默认或自定义 getter)
@property (nonatomic, assign, readonly) SEL getter;
// Setter 方法的选择器(非空,可能是默认或自定义 setter)
@property (nonatomic, assign, readonly) SEL setter;
/**
* 使用属性结构体创建信息对象
*
* @param property 底层属性结构体
* @return 新创建的属性信息对象
*/
- (instancetype)initWithProperty:(objc_property_t)property;
@end
property_t:
struct property_t {
const char *name; // 名称
const char *attributes; // 修饰
};
YYClassInfo
@interface YYClassInfo : NSObject
// 当前类对象(例如 [UIView class])
@property (nonatomic, assign, readonly) Class cls;
// 超类对象(例如 UIView 的 superCls = UIResponder)
@property (nullable, nonatomic, assign, readonly) Class superCls;
// 元类对象(存储类方法信息)
@property (nullable, nonatomic, assign, readonly) Class metaCls;
// 标记是否是元类(YES:元类信息,NO:类实例信息)
@property (nonatomic, readonly) BOOL isMeta;
// 类的名称字符串(例如 @"UIView")
@property (nonatomic, strong, readonly) NSString *name;
// 父类的完整元数据信息(递归结构)
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo;
// 所有实例变量的字典(key:变量名, value:YYClassIvarInfo)
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos;
// 所有方法的字典(key:方法名, value:YYClassMethodInfo)
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos;
// 所有属性的字典(key:属性名, value:YYClassPropertyInfo)
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos;
// 标记元数据需要更新(当类结构变化后调用)
- (void)setNeedUpdate;
// 检查元数据是否需要更新
- (BOOL)needUpdate;
// 通过类对象获取元数据(例如 [UIView class])
+ (nullable instancetype)classInfoWithClass:(Class)cls;
// 通过类名字符串获取元数据(例如 @"UIView")
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;
@end
- objc_class:
// objc.h
typedef struct objc_class *Class;
// runtime.h
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY; // isa 指针
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE; // 父类(超类)指针
const char * _Nonnull name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 版本
long info OBJC2_UNAVAILABLE; // 信息
long instance_size OBJC2_UNAVAILABLE; // 初始尺寸
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; // 变量列表
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; // 方法列表
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; // 缓存
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; // 协议列表
#endif
} OBJC2_UNAVAILABLE;
_YYModelPropertyMeta和 _YYModelMeta(NSObject+YYModel)
_YYModelPropertyMeta 对象的声明定义:
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name; ///< 属性名称
YYEncodingType _type; ///< 属性类型
YYEncodingNSType _nsType; ///< 属性在 Foundation 框架中的类型
BOOL _isCNumber; ///< 是否为 CNumber
Class _cls; ///< 属性类
Class _genericCls; ///< 属性包含的泛型类型,没有则为 nil
SEL _getter; ///< getter
SEL _setter; ///< setter
BOOL _isKVCCompatible; ///< 如果可以使用 KVC 则返回 YES
BOOL _isStructAvailableForKeyedArchiver; ///< 如果可以使用 archiver/unarchiver 归/解档则返回 YES
BOOL _hasCustomClassFromDictionary; ///< 类/泛型自定义类型,例如需要在数组中实现不同类型的转换需要用到
/*
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,如果没有映射到 keyPath 则返回 nil
NSArray *_mappedToKeyArray; ///< key 或者 keyPath 的数组,如果没有映射多个键的话则返回 nil
YYClassPropertyInfo *_info; ///< 属性信息,详见上文 YYClassPropertyInfo && property_t 章节
_YYModelPropertyMeta *_next; ///< 如果有多个属性映射到同一个 key 则指向下一个模型属性元
}
@end
_YYModelMeta 对象的声明定义:
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo;
/// Key:被映射的 key 与 keyPath, Value:_YYModelPropertyMeta.
NSDictionary *_mapper;
/// Array<_YYModelPropertyMeta>, 当前模型的所有 _YYModelPropertyMeta 数组
NSArray *_allPropertyMetas;
/// Array<_YYModelPropertyMeta>, 被映射到 keyPath 的 _YYModelPropertyMeta 数组
NSArray *_keyPathPropertyMetas;
/// Array<_YYModelPropertyMeta>, 被映射到多个 key 的 _YYModelPropertyMeta 数组
NSArray *_multiKeysPropertyMetas;
/// 映射 key 与 keyPath 的数量,等同于 _mapper.count
NSUInteger _keyMappedCount;
/// 模型 class 类型
YYEncodingNSType _nsType;
///作用:判断YYModel一系列协议方法是否实现
BOOL _hasCustomWillTransformFromDictionary;//解析前是否需要更改字典
BOOL _hasCustomTransformFromDictionary;//字典转模型后是否需要补充处理
BOOL _hasCustomTransformToDictionary;//模型转字典后是否需要补充处理
BOOL _hasCustomClassFromDictionary;//是否需要根据dic的内容转换为不同类型的模型
}
@end
我们换一种更形象的方式来解释这两个类的作用。可以把整个YYModel的转换过程想象成一个快递分拣系统。
场景设定:快递分拣中心(YYModel转换系统)
- 快递包裹:就是输入的JSON数据
- 收货地址:就是我们要转换成的模型对象
- 分拣规则:如何把包裹里的物品放到正确的地址
1. _YYModelPropertyMeta(分拣员小卡片)
想象每个收货地址(模型属性)都有一个分拣小卡片,上面写着:
收货地址:username (属性名)
分拣规则:
- 如果看到包裹上写着"user_name",就把这个包裹送这里 (映射规则1)
- 或者包裹上写着"login_name",也送这里(映射规则2)
- 需要特别处理:这个地址只收字符串包裹(类型要求)
- 备用方案:如果没找到包裹,用默认值"游客"(默认值)
实际对应的属性声明:
@property (nonatomic, copy) NSString *username;
JSON示例:
{
"user_name": "张小明",
"login_name": "zhangxiaoming"
}
小卡片上记录的关键信息:
-
地址名称:
username -
映射规则:
_mappedToKeyArray = @[@"user_name", @"login_name"] // 多个可能的来源 _type = 字符串类型
2. _YYModelMeta(总控台)
总控台掌控整个分拣中心:
当前处理区域:用户信息区(对应User类)
区域包含地址:
- username (分拣小卡片1)
- age (分拣小卡片2)
- address (分拣小卡片3)
特殊区域:
- 需要走特殊通道的地址:[需要层层拆箱的地址]
- 需要合并处理的地址:[一个地址对应多个包裹来源的]
今日特别要求:
- 需要先消毒再分拣:YES (自定义预处理)
- 分拣后要质检:YES (自定义后处理)
总控台做的事:
-
提前把每个地址的分拣小卡片准备好(创建属性元数据)
-
把所有可能出现的包裹标识(JSON键)做成索引:
"user_name" -> 分拣小卡片1 "login_name" -> 分拣小卡片1 "age" -> 分拣小卡片2 "user_age" -> 分拣小卡片2 -
特殊通道准备:
需要拆箱的地址:address(因为address是个复杂对象) 来源可能是:"user.address" 或 "address_info"
3. 实际分拣过程(转换过程)
当来了一批JSON包裹:
{
"user_name": "张小明",
"user_age": 18,
"address_info": {
"city": "北京",
"street": "朝阳区"
}
}
总控台处理流程:
- 先检查是否需要消毒(调用自定义预处理方法)
- 扫描每个包裹标签(遍历JSON键值对):
- 看到"user_name"标签:
查索引表 -> 对应分拣小卡片1(username)
把"张小明"送到username地址 - 看到"user_age"标签:
查索引表 -> 对应分拣小卡片2(age)
把18转成数字送到age地址 - 看到"address_info"标签:
发现需要走特殊通道(因为address是个对象)
拆开包裹,发现里面还有小包裹:
{“city”:“北京”, “street”:“朝阳区”}
用Address类的分拣系统继续处理(递归)
- 看到"user_name"标签:
- 全部完成后做质检(调用自定义后处理方法)
为什么需要这么复杂的设计?
当你面对这样的JSON时:
{
"user": {
"base_info": {
"name": "张小明",
"name_cn": "张小铭"
},
"age": 18
},
"contact": {
"phone": "13800138000"
}
}
而你想映射到这样的模型:
@interface MyModel : NSObject
@property NSString *name; // 映射到 user.base_info.name 或 user.base_info.name_cn
@property NSInteger age; // 映射到 user.age
@property NSString *phone; // 映射到 contact.phone
@end
分拣小卡片(_YYModelPropertyMeta)上会记录:
- name属性:可接受key路径
user.base_info.name或user.base_info.name_cn - age属性:映射到
user.age - phone属性:映射到
contact.phone
总控台(_YYModelMeta)会预先:
-
解析出name属性需要特殊处理(key路径)
-
建立快速索引:
"user.base_info.name" -> name属性 "user.base_info.name_cn" -> name属性 "user.age" -> age属性 "contact.phone" -> phone属性
当遇到复杂映射时,这种预编译的索引系统比每次转换时都去查找快得多,尤其当数据量很大时(比如1000条数据),性能差异非常明显。
YYModel实现json转model的源码逻辑

+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
/// 获取当前模型类
Class cls = [self class];
///2、通过class 获取到各种信息,然后封装到_YYModelMeta中
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
///是否需要根据字典内容修改模型类
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
///模型类实例化
NSObject *one = [cls new];
///3、实现JSON转模型功能
if ([one yy_modelSetWithDictionary:dictionary]) return one;
return nil;
}
metaWithClass
就是简单的从缓存之中获取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;
}
initWithClass
整理映射关系
- (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];
}
}
// 处理Model之中嵌套Model的操作
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
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;
}
}
// 创建属性元类
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { // 按照继承链创建属性元类,直到根类(NSObject/NSProxy)
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;
//用于创建并配置表示模型属性的元数据对象。它负责将原始的属性信息(YYClassPropertyInfo)转换为 YYModel 可以理解和处理的内部格式。
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
if (!meta || !meta->_name) continue;
if (!meta->_getter || !meta->_setter) continue;
if (allPropertyMetas[meta->_name]) continue;
allPropertyMetas[meta->_name] = meta;
}
curClassInfo = curClassInfo.superClassInfo;
}
//有值就进行复制
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
//
NSMutableDictionary *mapper = [NSMutableDictionary new];//直接映射
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];//路径映射
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];//多映射
//检测到实现modelCustomPropertyMapper就进入修改映射关系
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]]) {
if (mappedToKey.length == 0) return;
propertyMeta->_mappedToKey = mappedToKey;
NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
for (NSString *onePath in keyPath) {
if (onePath.length == 0) {
NSMutableArray *tmp = keyPath.mutableCopy;
[tmp removeObject:@""];
keyPath = tmp;
break;
}
}
if (keyPath.count > 1) {
propertyMeta->_mappedToKeyPath = keyPath;
[keyPathPropertyMetas addObject:propertyMeta];
}
//使用链表,可出现同一个值映射到多个属性
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
//处理多值映射
else if ([mappedToKey isKindOfClass:[NSArray class]]) {
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;
//_mappedToKeyArray添加内容
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
[multiKeysPropertyMetas addObject:propertyMeta];
//链表处理冲突
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
}];
}
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
//存储属性名
propertyMeta->_mappedToKey = name;
propertyMeta->_next = mapper[name] ?: nil;
mapper[name] = propertyMeta;
}];
if (mapper.count) _mapper = mapper;
if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
//这段代码是 _YYModelMeta 初始化的最终阶段,负责设置模型的全局特性和能力标志位。这些标志位对模型的行为有决定性影响,
_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;
}
其中着重讲两个我不太会的点
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
//设置映射路径
propertyMeta->_mappedToKey = name;
//向链表里添加内容
propertyMeta->_next = mapper[name] ?: nil;
mapper[name] = propertyMeta;
}];
假设我们现在有,多个属性需要使用同一个数据,单用一个map肯定是无法存储,所以YYModel使用了链表来解决哈希冲突,我们用以下例子:
@{
@"nickname": @"name", // 第一次处理
@"realName": @"name", // 第二次处理
@"fullName": @"name" // 第三次处理
}
-
第一个属性处理(例如
nickname映射到name):// 初始状态:mapper["name"] = nil propertyMeta_nickname->_next = nil; // 因为 mapper["name"] 为 nil mapper["name"] = propertyMeta_nickname; // 现在 mapper["name"] 指向 nickname 属性结果:
mapper: { "name": propertyMeta_nickname } propertyMeta_nickname->_next = nil -
第二个属性处理(例如
realName也映射到name):// 当前 mapper["name"] 存在值(指向 propertyMeta_nickname) propertyMeta_realName->_next = mapper["name"]; // 即 propertyMeta_realName->_next = propertyMeta_nickname mapper["name"] = propertyMeta_realName; // 更新头指针结果:
mapper: { "name": propertyMeta_realName } 链表结构:propertyMeta_realName -> propertyMeta_nickname -> nil
yy_modelSetWithDictionary
真正将Json数据转化为类信息的
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
//判空处理
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
//获取Meta
_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;
}
//定义一个 C 结构体 ModelSetContext,用于传递上下文信息给底层的 C 函数。
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
//判断模型属性数是否大于等于字典键的数量,哪个小用哪个
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
//使用 Core Foundation 的 CFDictionaryApplyFunction 高效遍历字典 dic 中的所有键值对。对每一个键值对,调用 ModelSetWithDictionaryFunction 函数,将字典中的值赋给模型对应的属性。
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
if (modelMeta->_keyPathPropertyMetas) {
//使用 CFArrayApplyFunction 遍历嵌套路径,调用 ModelSetWithPropertyMetaArrayFunction 处理赋值。
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);
}
//如果有一些特殊的属性,需要自己转换赋值的话,再处理一下
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
CFDictionaryApplyFunction和CFArrayApplyFunction
CFDictionaryApplyFunction和CFArrayApplyFunction在官方文档里有解释:
// Calls a function once for each key-value pair in a dictionary.
// 对于字典里的每一个键值对,都会调用一次applier 方法
void CFDictionaryApplyFunction(CFDictionaryRef theDict, CFDictionaryApplierFunction applier, void *context);
//Calls a function once for each element in range in an array。
// 对于数组中指定range返回的每一个元素调用一次applier
void CFArrayApplyFunction(CFArrayRef theArray, CFRange range, CFArrayApplierFunction applier, void *context);
ModelSetWithDictionaryFunction
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
ModelSetContext *context = _context;
// 1.从上下文中取到model 的信息
__unsafe_unretained _YYModelMeta meta = (__bridge _YYModelMeta )(context->modelMeta);
// 2.从转换字典中取到属性对象
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
__unsafe_unretained id model = (__bridge id)(context->model);
// 3.以防有多个相同key 的不同值
while (propertyMeta) {
if (propertyMeta->_setter) {
// 为model 的该属性赋值。
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
propertyMeta = propertyMeta->_next;
};
}
上下文结构:
typedef struct {
void *modelMeta; // 模型元数据 (_YYModelMeta)
void *model; // 当前模型对象
void *dictionary; // 原始字典
} ModelSetContext;
而ModelSetValueForProperty方法中会根据属性的类型调用objc_msgSend来赋相应类型的值。例如字符串类型的赋值:
if (meta->_nsType == YYEncodingTypeNSString) {
((void ()(id, SEL, id))(void ) objc_msgSend)((id)model, meta->_setter, value);
}
ModelSetWithPropertyMetaArrayFunction
从array之中按照映射进行赋值
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) {
value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
} else if (propertyMeta->_mappedToKeyPath) {
value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
} else {
value = [dictionary objectForKey:propertyMeta->_mappedToKey];
}
if (value) {
__unsafe_unretained id model = (__bridge id)(context->model);
ModelSetValueForProperty(model, value, propertyMeta);
}
}
916

被折叠的 条评论
为什么被折叠?



