当前位置: 首页 > news >正文

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 {@packageNSString *_name;             ///< 属性名称YYEncodingType _type;        ///< 属性类型YYEncodingNSType _nsType;    ///< 属性在 Foundation 框架中的类型BOOL _isCNumber;             ///< 是否为 CNumberClass _cls;                  ///< 属性类Class _genericCls;           ///< 属性包含的泛型类型,没有则为 nilSEL _getter;                 ///< getterSEL _setter;                 ///< setterBOOL _isKVCCompatible;       ///< 如果可以使用 KVC 则返回 YESBOOL _isStructAvailableForKeyedArchiver; ///< 如果可以使用 archiver/unarchiver 归/解档则返回 YESBOOL _hasCustomClassFromDictionary; ///< 类/泛型自定义类型,例如需要在数组中实现不同类型的转换需要用到/*property->key:       _mappedToKey:key     _mappedToKeyPath:nil            _mappedToKeyArray:nilproperty->keyPath:   _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nilproperty->keys:      _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath    _mappedToKeyArray:keys(array)*/NSString *_mappedToKey;      ///< 映射 keyNSArray *_mappedToKeyPath;   ///< 映射 keyPath,如果没有映射到 keyPath 则返回 nilNSArray *_mappedToKeyArray;  ///< key 或者 keyPath 的数组,如果没有映射多个键的话则返回 nilYYClassPropertyInfo *_info;  ///< 属性信息,详见上文 YYClassPropertyInfo && property_t 章节_YYModelPropertyMeta *_next; ///< 如果有多个属性映射到同一个 key 则指向下一个模型属性元
}
@end

_YYModelMeta 对象的声明定义:

@interface _YYModelMeta : NSObject {@packageYYClassInfo *_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.countNSUInteger _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:classInfopropertyInfo:propertyInfogeneric: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);}
}
http://www.dtcms.com/a/496933.html

相关文章:

  • wordpress缩略图生成网站怎么做推广和优化
  • 怎样做网站关键词优化成全视频免费观看在线看2024年新年贺词
  • 那些网站分享pr做的视频软件制作商城网站模板
  • 网站开发的响应式和兼容性问题wordpress页面权限设置
  • c.xxl.job.core.log.XxlJobFileAppender : No such file or directory
  • 网站还没建设好可以备案吗网站开发之前前后端不分离
  • 米拓建站教程wordpress 视频收费
  • 网站半年了 没有流量网站备案怎么换
  • 网站内容页显示不出来建个企业网站备案需要多长时间
  • 重建大师8.0 | 首创OPGS-Mesh建模技术,三维模型走向美用兼得新态势
  • 建设银行网站怎么修改手机号码吗电子商务网站规划与建设论文
  • 做语文高考题网站企业展厅怎么设计
  • 湛江网站设计软件庆阳网站哪里做
  • 宠物店信息管理系统25nfryp8(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 郑州网站制作的公司哪家好那家做网站好
  • 一套针对金融领域多模态问答的自适应多层级RAG框架-VeritasFi
  • 建设银行网站上的的研究报告全国大型教育集团网站建设
  • 做网站有送企业邮箱吗全屋定制官网
  • 毕设做网站什么主题比较好织梦网站怎么做模板
  • 【图像超分】python实现制作超分辨率数据集
  • 网页pc端广州关键词优化外包
  • 泊头西环网站建设宁德seo
  • 南宁网站设计多少钱一个WordPress自带的博客
  • 模电基础:反馈的基本概念及组态识别
  • 哪个网站可以做汽车评估营销推广策划及渠道
  • 什么网站可以教做面包合肥市城乡建设局网站首页
  • Transformer的核心组成:编码器(Encoder)和解码器(Decoder)
  • 可以做英文教师的网站有趣的个人网站
  • 建设小学瓯江小区网站百度app官方下载
  • 深度解析 mysql 作为向量数据库如何存储和查询向量数据