完整教程:YYModel源码学习

YYModel源码学习

YYModel的代码结构

img

YYClassInfo

img

YYClassInfo 主要将 Runtime 层级的一些结构体封装到 NSObject 层级以便调用,以下为对应内容

YYClassInfoRuntime
YYClassIvarInfoobjc_ivar
YYClassMethodInfoobjc_method
YYClassPropertyInfoproperty_t
YYClassInfoobjc_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 (自定义后处理)

总控台做的事

  1. 提前把每个地址的分拣小卡片准备好(创建属性元数据)

  2. 把所有可能出现的包裹标识(JSON键)做成索引:

    "user_name" -> 分拣小卡片1
    "login_name" -> 分拣小卡片1
    "age"        -> 分拣小卡片2
    "user_age"   -> 分拣小卡片2
  3. 特殊通道准备:

    需要拆箱的地址:address(因为address是个复杂对象)
    来源可能是:"user.address" 或 "address_info"

3. 实际分拣过程(转换过程)

当来了一批JSON包裹

{
"user_name": "张小明",
"user_age": 18,
"address_info": {
"city": "北京",
"street": "朝阳区"
}
}

总控台处理流程

  1. 先检查是否需要消毒(调用自定义预处理方法)
  2. 扫描每个包裹标签(遍历JSON键值对):
    • 看到"user_name"标签:
      查索引表 -> 对应分拣小卡片1(username)
      把"张小明"送到username地址
    • 看到"user_age"标签:
      查索引表 -> 对应分拣小卡片2(age)
      把18转成数字送到age地址
    • 看到"address_info"标签:
      发现需要走特殊通道(因为address是个对象)
      拆开包裹,发现里面还有小包裹:
      {“city”:“北京”, “street”:“朝阳区”}
      用Address类的分拣系统继续处理(递归)
  3. 全部完成后做质检(调用自定义后处理方法)

为什么需要这么复杂的设计?

当你面对这样的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.nameuser.base_info.name_cn
  • age属性:映射到 user.age
  • phone属性:映射到 contact.phone

总控台(_YYModelMeta)会预先

  1. 解析出name属性需要特殊处理(key路径)

  2. 建立快速索引:

    "user.base_info.name" -> name属性
    "user.base_info.name_cn" -> name属性
    "user.age" -> age属性
    "contact.phone" -> phone属性

当遇到复杂映射时,这种预编译的索引系统比每次转换时都去查找快得多,尤其当数据量很大时(比如1000条数据),性能差异非常明显。

YYModel实现json转model的源码逻辑

img

+ (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"        // 第三次处理
}
  1. 第一个属性处理(例如 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
  2. 第二个属性处理(例如 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

  • CFDictionaryApplyFunctionCFArrayApplyFunction在官方文档里有解释:
// 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);
}
}
posted @ 2026-02-09 11:53  gccbuaa  阅读(12)  评论(0)    收藏  举报