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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值