【iOS】YYModel源码解析
YYModel源码解析
文章目录
- YYModel源码解析
- 前言
- YYModel性能优势
- YYModel简介
- YYClassInfo解析
- YYClassIvarInfo && objc_ivar
- YYClassMethodInfo && objc_method
- YYClassPropertyInfo && property_t
- YYClassInfo && objc_class
- YYClassInfo的初始化细节
- NSObject+YYModel 探究
- 类型编码解析
- 数据结构的定义
- _YYModelPropertyMeta
- _YYModelMeta
- JSON->Model
- JSON->Dictionary
- NSDictionary->Model
- Model->JSON
- 总结
前言
最近学习一些有关于第三方库源码的内容,把YYModel和JSONModel这两个第三方库的内容看完
YYModel性能优势
YYModel 是一个非常轻量级的 JSON 模型自动转换库,代码风格良好且思路清晰,可以从源码中看到作者对 Runtime 深厚的理解。难能可贵的是 YYModel 在其轻量级的代码下还保留着自动类型转换,类型安全,无侵入等特性,并且具有接近手写解析代码的超高性能。
YYModel简介
这里其实我们可以看出YYModel的结构其实是比较简单的,其实只有YYClassInfo
和NSObject+YYModel
两个模块
- 前者将Runtime层级的一些结构体封装到NSObject层以便调用
- 后者负责提供方便调用的接口以及实现具体的模型转换逻辑
YYClassInfo解析
这里我们看一张图来对比理解这里的YYClassInfo
和Runtime
层级的一些结构体的关系:
YYClassInfo | Runtime |
---|---|
YYClassIvarInfo | objc_ivar |
YYClassMethodInfo | objc_method |
YYClassPropertyInfo | property_t |
YYClassInfo | objc_class |
下面我会一一对比两者直接的关系:
YYClassIvarInfo && objc_ivar
@interface YYClassIvarInfo : NSObject
@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct 变量的结构体
@property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name 变量名称
@property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset 变量的偏移量
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding 变量类型编码
@property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type 变量类型,通过YYEncodingGetType方法从类型编码中取得/**Creates and returns an ivar info object.@param ivar ivar opaque struct@return A new object, or nil if an error occurs.*/
- (instancetype)initWithIvar:(Ivar)ivar;
@end//对应的objc_var
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
}
这里我们发现这里的NSString
类型都被strong
来修饰,因为所有的字符串都是通过Runtime方法拿到的const char *
之后通过stringWithUTF8String
方法转化成NSString的,这里使用strong做一次单纯的强引用在性能上会比copy高
YYClassMethodInfo && objc_method
@interface YYClassMethodInfo : NSObject
@property (nonatomic, assign, readonly) Method method; ///< method opaque struct // 方法
@property (nonatomic, strong, readonly) NSString *name; ///< method name // 方法名
@property (nonatomic, assign, readonly) SEL sel; ///< method's selector //sel
@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation //IMP指针
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types // 方法参数和方法类型
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type // 返回值类型编码
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type // 参数类型编码
//源码
struct objc_method {SEL _Nonnull method_name OBJC2_UNAVAILABLE; // 方法名称char * _Nullable method_types OBJC2_UNAVAILABLE; // 方法类型IMP _Nonnull method_imp OBJC2_UNAVAILABLE; // 方法实现(函数指针)
}
sel 可以理解为方法选择器,也就是映射到方法的C字符串
imp 也就是指向方法具体实现逻辑的函数指针
YYClassPropertyInfo && property_t
@interface YYClassPropertyInfo : NSObject
@property (nonatomic, assign, readonly) objc_property_t property; ///< 属性
@property (nonatomic, strong, readonly) NSString *name; ///< 属性名称
@property (nonatomic, assign, readonly) YYEncodingType type; ///< 属性类型
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< 属性类型编码
@property (nonatomic, strong, readonly) NSString *ivarName; ///< 变量名称
@property (nullable, nonatomic, assign, readonly) Class cls; ///< 类型
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< 属性相关协议
@property (nonatomic, assign, readonly) SEL getter; ///< getter 方法选择器
@property (nonatomic, assign, readonly) SEL setter; ///< setter 方法选择器- (instancetype)initWithProperty:(objc_property_t)property;
@endstruct property_t {const char *name; // 名称const char *attributes; // 修饰
};
可以理解成对于YYClassPropertyInfo
是作者对于property_t
封装,注意一下对于getter和setter方法的封装
//先尝试获取属性的getter和setter方法
case 'G': {type |= YYEncodingTypePropertyCustomGetter;if (attrs[i].value) {_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);}} break;case 'S': {type |= YYEncodingTypePropertyCustomSetter;if (attrs[i].value) {_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);}} // break; commented for code coverage in next line
// 如果没有则按照标准规则自己造
if (!_getter) {_getter = NSSelectorFromString(_name);}if (!_setter) {_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);}
YYClassInfo && objc_class
最后YYModel还封装了objc_class
,objc_class
在Runtime中表示一个OC类.
@interface YYClassInfo : NSObject
@property (nonatomic, assign, readonly) Class cls; ///< 类
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< 超类
@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< 元类
@property (nonatomic, readonly) BOOL isMeta; ///< 元类标识,自身是否为元类
@property (nonatomic, strong, readonly) NSString *name; ///< 类名称
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< 父类(超类)信息
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< 变量信息
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< 方法信息
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< 属性信息- (void)setNeedUpdate;
- (BOOL)needUpdate;+ (nullable instancetype)classInfoWithClass:(Class)cls;
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;@end// 源码部分
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;
这里可以看到他对于每一个类对象里面所有内容进行封装.
上面简单介绍了一下这几个核心结构体和Runtime的底层的对于关系,以及一个处理
YYClassInfo的初始化细节
我们这里是把objc_class
在外层封装成一个YYClassInfo
,这里就简单看一下这个初始化函数
+ (instancetype)classInfoWithClass:(Class)cls {// 判空入参if (!cls) return nil;// 单例缓存 classCache 与 metaCache,对应缓存类和元类static CFMutableDictionaryRef classCache;static CFMutableDictionaryRef metaCache;static dispatch_once_t onceToken;static dispatch_semaphore_t lock;dispatch_once(&onceToken, ^{classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);// 这里把 dispatch_semaphore 当做锁来使用(当信号量只有 1 时)lock = dispatch_semaphore_create(1);});// 初始化之前,首先会根据当前 YYClassInfo 是否为元类去对应的单例缓存中查找// 这里使用了上面的 dispatch_semaphore 加锁,保证单例缓存的线程安全 dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));// 如果找到了,且找到的信息需要更新的话则执行更新操作if (info && info->_needUpdate) { // 这个如果发生类信息发生变化,就进行一次加载[info _update];}dispatch_semaphore_signal(lock);// 如果没找到,才会去老实初始化if (!info) {info = [[YYClassInfo alloc] initWithClass:cls];if (info) { // 初始化成功// 线程安全dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);// 根据初始化信息选择向对应的类/元类缓存注入信息,key = cls,value = infoCFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));dispatch_semaphore_signal(lock);}}return info;
}
- 创建单利缓存,类缓存和元类缓存
- 使用
信号量(dispatch_semaphore)
作为锁保证缓存线程安全 - 初始化前先去缓存中查找是否已经象缓存中注册当前要初始化的YYClassInfo
- 如果查找到缓存对象,则判断缓存对象是否需要更新并执行相关操作
- 如果缓存中为找到缓存对象则初始化
- 初始化完成后向缓存中注册
YYClassInfo
实例
NSObject+YYModel 探究
这里是利用YYClassInfo层级封装好的类切实的执行JSON模型的有一个转换逻辑,并且提供类一个无侵入性的接口,主要分成了四个模块实现对于JSON的逻辑转换
- 类型编码解析
- 数据结构定义
- 递归模型转换
- 接口相关代码
类型编码解析
类型编码解析代码主要集中在 NSObject+YYModel.m 的上面部分,涉及到 YYEncodingNSType
枚举的定义,配套 YYClassGetNSType
函数将 NS 类型转为 YYEncodingNSType 还有 YYEncodingTypeIsCNumber
函数判断类型是否可以直接转为 C 语言数值类型的函数。
typedef NS_ENUM (NSUInteger, YYEncodingNSType) {YYEncodingTypeNSUnknown = 0,YYEncodingTypeNSString,YYEncodingTypeNSMutableString,YYEncodingTypeNSValue,YYEncodingTypeNSNumber,YYEncodingTypeNSDecimalNumber,YYEncodingTypeNSData,YYEncodingTypeNSMutableData,YYEncodingTypeNSDate,YYEncodingTypeNSURL,YYEncodingTypeNSArray,YYEncodingTypeNSMutableArray,YYEncodingTypeNSDictionary,YYEncodingTypeNSMutableDictionary,YYEncodingTypeNSSet,YYEncodingTypeNSMutableSet,
};static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {if (!cls) return YYEncodingTypeNSUnknown;if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;return YYEncodingTypeNSUnknown;
}
//这两个部分的内容实现将cls快速转化成枚举值,方便后面对于不同类型进行一个处理
//认识一下inline这个内容:主要就是编译器将函数体直接插入调用出,而不是生成函数调用,减小函数调用的时候的开销,就相当于减少了一个函数栈帧插入的一个过程
这里我们看一下有关于将YYNSBlockClass
指向了NSBlock
类,实现过程如下:
static force_inline Class YYNSBlockClass() {static Class cls;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{void (^block)(void) = ^{}; // 创建一个空的Blockcls = ((NSObject *)block).class; //获得指针while (class_getSuperclass(cls) != [NSObject class]) { // 向上查找cls = class_getSuperclass(cls);}});return cls; // current is "NSBlock"
}
// block的类结构大致如下:
__NSGlobalBlock__↓
__NSBlock__ // 到这一层就不再查找,退出,然后给他赋值给我们的YYNSBlockClass执行NSBlock↓
NSObject
数据结构的定义
NSObject+YYModel 中重新定义了两个类,通过它们来使用 YYClassInfo 中的封装。
NSObject+YYModel | YYClassInfo |
---|---|
_YYModelPropertyMeta | YYClassPropertyInfo |
_YYModelMeta | YYClassInfo |
_YYModelPropertyMeta
这里是一个模型对象中的属性信息,它包含YYClassPropertyInfo
@interface _YYModelPropertyMeta : NSObject {@packageNSString *_name; ///< property's name 属性名称YYEncodingType _type; ///< property's type 属性类型YYEncodingNSType _nsType; ///< property's Foundation type 框架中的类型BOOL _isCNumber; ///< is c number type 是否为CNumber, 也就是判断是不是一个基本的类型Class _cls; ///< property's class, or nil 属性类Class _genericCls; ///< container's generic class, or nil if threr's no generic class 属性包含的泛型类型,没有则为 nilSEL _getter; ///< getter, or nil if the instances cannot respond getter方法SEL _setter; ///< setter, or nil if the instances cannot respond setter方法BOOL _isKVCCompatible; ///< YES if it can access with key-value coding 如果可以使用KVC就可以返回YESBOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver // 如果可以使用 archiver/unarchiver 归/解档则返回 YESBOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:// 类/泛型自定义类型,例如需要在数组中实现不同类型的转换需要用到/*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; ///< the key mapped to 映射keyNSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path) 映射keyPath则返回nilNSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys) key或者keyPath数组,如果没有映射多个key的话则返回nilYYClassPropertyInfo *_info; ///< property's info 属性信息_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key. //如果有多个属性映射到同一个key 则执行下一个模型属性元
}
_YYModelMeta
这里表示的是模型对象中的属性信息:
@interface _YYModelMeta : NSObject {@packageYYClassInfo *_classInfo; /// Key:mapped key and key path, Value:_YYModelPropertyMeta.NSDictionary *_mapper; // 当前字典的KEY 与KeyPath Value:_YYModelPropertyMeta的内容/// Array<_YYModelPropertyMeta>, all property meta of this model.NSArray *_allPropertyMetas; // 当前Model所有的 _YYModelPropertyMeta/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.NSArray *_keyPathPropertyMetas; // 被映射到 keyPath 的 _YYModelPropertyMeta 数组/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.NSArray *_multiKeysPropertyMetas; // 被映射到 key 的 _YYModelPropertyMeta 数组/// The number of mapped key (and key path), same to _mapper.count.NSUInteger _keyMappedCount; // key 与keyPath的数量/// Model class type.YYEncodingNSType _nsType; // class的类型BOOL _hasCustomWillTransformFromDictionary;BOOL _hasCustomTransformFromDictionary;BOOL _hasCustomTransformToDictionary;BOOL _hasCustomClassFromDictionary;
}
JSON->Model
这里我们从转化的起点开始学习
+ (instancetype)yy_modelWithJSON:(id)json {// 将 json 转为字典 dicNSDictionary *dic = [self _yy_dictionaryWithJSON:json];// 将json转化为Modelreturn [self yy_modelWithDictionary:dic];
}
这里我们把这个步骤转化成了两个步骤:
JSON->Dictionary
这里我们在看一下有关于yy_dictionaryWithJSON
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {//入参判空if (!json || json == (id)kCFNull) return nil;NSDictionary *dic = nil;NSData *jsonData = nil;if ([json isKindOfClass:[NSDictionary class]]) {// 如果是 NSDictionary 类则直接赋值dic = json;} else if ([json isKindOfClass:[NSString class]]) {// 如果是 NSString 类则用 UTF-8 编码转 NSDatajsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];} else if ([json isKindOfClass:[NSData class]]) {//如果是data直接赋值jsonData = json;}if (jsonData) {// 利用 NSJSONSerialization 方法将 jsonData 转为 dicdic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];if (![dic isKindOfClass:[NSDictionary class]]) dic = nil; // 判断转换结果 }return dic;
}
其中 kCFNull
是 CoreFoundation 中 CFNull 的单例对象。如同 Foundation 框架中的 NSNull 一样,CFNull 是用来表示集合对象中的空值(不允许为 NULL)。CFNull 对象既不允许被创建也不允许被销毁,而是通过定义一个 CFNull 常量,即 kCFNull
,在需要空值时使用。
NSDictionary->Model
这里我们再看第二个步骤:
+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {if (!dictionary || dictionary == (id)kCFNull) return nil;if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;Class cls = [self class];// 使用当前类生成一个 _YYModelMeta 模型元类_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];// 这里 _hasCustomClassFromDictionary 用于标识是否需要自定义返回类if (modelMeta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;}// 调用 yy_modelSetWithDictionary 为新建的类实例 one 赋值,赋值成功则返回 oneNSObject *one = [cls new];if ([one yy_modelSetWithDictionary:dictionary]) return one;return nil;
}
这里之后再进入下面这个方法yy_modelSetWithDictionary
这个方法可以说是模型转化的核心代码:
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {if (!dic || dic == (id)kCFNull) return NO;if (![dic isKindOfClass:[NSDictionary class]]) return NO;_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];//如果key没有就返回NOif (modelMeta->_keyMappedCount == 0) return NO;//是否有 modelCustomWillTransformFromDictionary 接口if (modelMeta->_hasCustomWillTransformFromDictionary) {dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];if (![dic isKindOfClass:[NSDictionary class]]) return NO;}//设置上下文内容ModelSetContext context = {0};context.modelMeta = (__bridge void *)(modelMeta);context.model = (__bridge void *)(self);context.dictionary = (__bridge void *)(dic);// 判断模型元键值映射数量与 JSON 所得字典的数量关系, 将dictionary的数据填充到我们的Model层上面if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {// 一般情况下他们的数量相等// 特殊情况比如有的属性元会映射字典中的多个 key// 为字典中的每个键值对调用 ModelSetWithDictionaryFunction// 这句话是核心代码,一般情况下就是靠 ModelSetWithDictionaryFunction 通过字典设置模型CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);// 处理keyPath的属性if (modelMeta->_keyPathPropertyMetas) {//对每一个keypath调用对应方法CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),ModelSetWithPropertyMetaArrayFunction,&context);}//处理映射多个 key 的属性元if (modelMeta->_multiKeysPropertyMetas) {CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),ModelSetWithPropertyMetaArrayFunction,&context);}} else {//处理单个key的方法CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,CFRangeMake(0, modelMeta->_keyMappedCount),ModelSetWithPropertyMetaArrayFunction,&context);}if (modelMeta->_hasCustomTransformFromDictionary) {return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];}return YES;
}
这里主要分成了下面几个步骤:
- 入参校验
- 初始化模型元以及映射表校验
- 初始化模型设置上下文
ModelSetContext
- 为字典中的每个键值对调用
ModelSetWithDictionaryFunction
- 检验转换结果
上下文的结构体如下图:
typedef struct {void *modelMeta; ///< 模型元void *model; ///< 模型实例,指向输出的模型void *dictionary; ///< 待转换字典
} ModelSetContext;
下面在看一下这个方法:ModelSetWithDictionaryFunction
:这一步可以理解为设置Model的逻辑
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {//获得入参上下文ModelSetContext *context = _context;//获得对应的模型元__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);//获得对应的属性元__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];//获得对应的一个未处理的model__unsafe_unretained id model = (__bridge id)(context->model);// 遍历 propertyMeta,直到 propertyMeta->_next == nilwhile (propertyMeta) {if (propertyMeta->_setter) { //如果有setter方法就采用setter,则调用 ModelSetValueForProperty 赋值ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);}propertyMeta = propertyMeta->_next;};
}
这个方法就是通过获取对应的一个属性元,找到 setter
不为空的属性元通过 ModelSetValueForProperty
方法赋值。
ModelSetValueForProperty
函数是为模型中的属性赋值的实现方法,也是整个 YYModel 的核心代码
static void ModelSetValueForProperty(__unsafe_unretained id model,__unsafe_unretained id value,__unsafe_unretained _YYModelPropertyMeta *meta) {if (meta->_isCNumber) {NSNumber *num = YYNSNumberCreateFromID(value);ModelSetNumberToProperty(model, num, meta);if (num) [num class]; // hold the number} else if (meta->_nsType) {if (value == (id)kCFNull) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);} else {switch (meta->_nsType) {case YYEncodingTypeNSString:case YYEncodingTypeNSMutableString: {if ([value isKindOfClass:[NSString class]]) {if (meta->_nsType == YYEncodingTypeNSString) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);}} else if ([value isKindOfClass:[NSNumber class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,(meta->_nsType == YYEncodingTypeNSString) ?((NSNumber *)value).stringValue :((NSNumber *)value).stringValue.mutableCopy);} else if ([value isKindOfClass:[NSData class]]) {NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding];((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string);} else if ([value isKindOfClass:[NSURL class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,(meta->_nsType == YYEncodingTypeNSString) ?((NSURL *)value).absoluteString :((NSURL *)value).absoluteString.mutableCopy);} else if ([value isKindOfClass:[NSAttributedString class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,(meta->_nsType == YYEncodingTypeNSString) ?((NSAttributedString *)value).string :((NSAttributedString *)value).string.mutableCopy);}} break;case YYEncodingTypeNSValue:case YYEncodingTypeNSNumber:case YYEncodingTypeNSDecimalNumber: {if (meta->_nsType == YYEncodingTypeNSNumber) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSNumberCreateFromID(value));} else if (meta->_nsType == YYEncodingTypeNSDecimalNumber) {if ([value isKindOfClass:[NSDecimalNumber class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else if ([value isKindOfClass:[NSNumber class]]) {NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);} else if ([value isKindOfClass:[NSString class]]) {NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:value];NSDecimal dec = decNum.decimalValue;if (dec._length == 0 && dec._isNegative) {decNum = nil; // NaN}((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);}} else { // YYEncodingTypeNSValueif ([value isKindOfClass:[NSValue class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);}}} break;case YYEncodingTypeNSData:case YYEncodingTypeNSMutableData: {if ([value isKindOfClass:[NSData class]]) {if (meta->_nsType == YYEncodingTypeNSData) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else {NSMutableData *data = ((NSData *)value).mutableCopy;((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);}} else if ([value isKindOfClass:[NSString class]]) {NSData *data = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding];if (meta->_nsType == YYEncodingTypeNSMutableData) {data = ((NSData *)data).mutableCopy;}((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);}} break;case YYEncodingTypeNSDate: {if ([value isKindOfClass:[NSDate class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else if ([value isKindOfClass:[NSString class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSDateFromString(value));}} break;case YYEncodingTypeNSURL: {if ([value isKindOfClass:[NSURL class]]) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else if ([value isKindOfClass:[NSString class]]) {NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];NSString *str = [value stringByTrimmingCharactersInSet:set];if (str.length == 0) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, nil);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, [[NSURL alloc] initWithString:str]);}}} break;case YYEncodingTypeNSArray:case YYEncodingTypeNSMutableArray: {if (meta->_genericCls) {NSArray *valueArr = nil;if ([value isKindOfClass:[NSArray class]]) valueArr = value;else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects;if (valueArr) {NSMutableArray *objectArr = [NSMutableArray new];for (id one in valueArr) {if ([one isKindOfClass:meta->_genericCls]) {[objectArr addObject:one];} else if ([one isKindOfClass:[NSDictionary class]]) {Class cls = meta->_genericCls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:one];if (!cls) cls = meta->_genericCls; // for xcode code coverage}NSObject *newOne = [cls new];[newOne yy_modelSetWithDictionary:one];if (newOne) [objectArr addObject:newOne];}}((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);}} else {if ([value isKindOfClass:[NSArray class]]) {if (meta->_nsType == YYEncodingTypeNSArray) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,((NSArray *)value).mutableCopy);}} else if ([value isKindOfClass:[NSSet class]]) {if (meta->_nsType == YYEncodingTypeNSArray) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,((NSSet *)value).allObjects.mutableCopy);}}}} break;case YYEncodingTypeNSDictionary:case YYEncodingTypeNSMutableDictionary: {if ([value isKindOfClass:[NSDictionary class]]) {if (meta->_genericCls) {NSMutableDictionary *dic = [NSMutableDictionary new];[((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) {if ([oneValue isKindOfClass:[NSDictionary class]]) {Class cls = meta->_genericCls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:oneValue];if (!cls) cls = meta->_genericCls; // for xcode code coverage}NSObject *newOne = [cls new];[newOne yy_modelSetWithDictionary:(id)oneValue];if (newOne) dic[oneKey] = newOne;}}];((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic);} else {if (meta->_nsType == YYEncodingTypeNSDictionary) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,((NSDictionary *)value).mutableCopy);}}}} break;case YYEncodingTypeNSSet:case YYEncodingTypeNSMutableSet: {NSSet *valueSet = nil;if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value];else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value);if (meta->_genericCls) {NSMutableSet *set = [NSMutableSet new];for (id one in valueSet) {if ([one isKindOfClass:meta->_genericCls]) {[set addObject:one];} else if ([one isKindOfClass:[NSDictionary class]]) {Class cls = meta->_genericCls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:one];if (!cls) cls = meta->_genericCls; // for xcode code coverage}NSObject *newOne = [cls new];[newOne yy_modelSetWithDictionary:one];if (newOne) [set addObject:newOne];}}((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, set);} else {if (meta->_nsType == YYEncodingTypeNSSet) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, valueSet);} else {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,meta->_setter,((NSSet *)valueSet).mutableCopy);}}} // break; commented for code coverage in next linedefault: break;}}} else {BOOL isNull = (value == (id)kCFNull);switch (meta->_type & YYEncodingTypeMask) {case YYEncodingTypeObject: {if (isNull) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);} else if ([value isKindOfClass:meta->_cls] || !meta->_cls) {((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);} else if ([value isKindOfClass:[NSDictionary class]]) {NSObject *one = nil;if (meta->_getter) {one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);}if (one) {[one yy_modelSetWithDictionary:value];} else {Class cls = meta->_cls;if (meta->_hasCustomClassFromDictionary) {cls = [cls modelCustomClassForDictionary:value];if (!cls) cls = meta->_genericCls; // for xcode code coverage}one = [cls new];[one yy_modelSetWithDictionary:value];((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);}}} break;case YYEncodingTypeClass: {if (isNull) {((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)NULL);} else {Class cls = nil;if ([value isKindOfClass:[NSString class]]) {cls = NSClassFromString(value);if (cls) {((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)cls);}} else {cls = object_getClass(value);if (cls) {if (class_isMetaClass(cls)) {((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)value);}}}}} break;case YYEncodingTypeSEL: {if (isNull) {((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)NULL);} else if ([value isKindOfClass:[NSString class]]) {SEL sel = NSSelectorFromString(value);if (sel) ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)sel);}} break;case YYEncodingTypeBlock: {if (isNull) {((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())NULL);} else if ([value isKindOfClass:YYNSBlockClass()]) {((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())value);}} break;case YYEncodingTypeStruct:case YYEncodingTypeUnion:case YYEncodingTypeCArray: {if ([value isKindOfClass:[NSValue class]]) {const char *valueType = ((NSValue *)value).objCType;const char *metaType = meta->_info.typeEncoding.UTF8String;if (valueType && metaType && strcmp(valueType, metaType) == 0) {[model setValue:value forKey:meta->_name];}}} break;case YYEncodingTypePointer:case YYEncodingTypeCString: {if (isNull) {((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, (void *)NULL);} else if ([value isKindOfClass:[NSValue class]]) {NSValue *nsValue = value;if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) {((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue); // 最后都会采用setter方法来给数据赋值}}} // break; commented for code coverage in next linedefault: break;}}
}
这个函数就是通过判断属性类型来对于model层的数据进行一个赋值操作
- 根据属性元类型划分代码逻辑
- 如果属性元是 CNumber 类型,即 int、uint 之类,则使用
ModelSetNumberToProperty
赋值 - 如果属性元属于 NSType 类型,即 NSString、NSNumber 之类,则根据类型转换中可能涉及到的对应类型做逻辑判断并赋值(可以去上面代码中查看具体实现逻辑)
- 如果属性元不属于 CNumber 和 NSType,则猜测为 id,Class,SEL,Block,struct、union、char[n],void或 char 类型并且做出相应的转换和赋值
这里最后都是采用有一个我们的属性的setter方法来调用的
这里有一位大佬给出了一个图:
Model->JSON
我们有从Model转向JSON那自然也有从JSON转向Model的内容,这里看这个函数:
- (id)yy_modelToJSONObject {/*Apple said:The top level object is an NSArray or NSDictionary.All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.All dictionary keys are instances of NSString.Numbers are not NaN or infinity.*/id jsonObject = ModelToJSONObjectRecursive(self);if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;return nil;
}
这里因为是采用的是NSJSONSerialization
来进行一个转化的,将一个对象model
转化成JSON数据
.他有一个下面的要求
-
顶级对象是 NSArray 或者 NSDictionary 类型
-
所有的对象都是 NSString, NSNumber, NSArray, NSDictionary, 或 NSNull 的实例
-
所有字典中的 key 都是一个 NSString 实例
-
Numbers 是除去无穷大和 NaN 的其他表示
这里我们简单看一下这个函数:这不罗列出详细的内容,简单介绍一下相关内容,这里是对于Model层的每一个元素进行一个递归检查的内容,
// 递归转换模型到 JSON,如果转换异常则返回 nil
static id ModelToJSONObjectRecursive(NSObject *model) {// 判空或者可以直接返回的对象,则直接返回if (!model || model == (id)kCFNull) return model;if ([model isKindOfClass:[NSString class]]) return model;if ([model isKindOfClass:[NSNumber class]]) return model;// 如果 model 从属于 NSDictionaryif ([model isKindOfClass:[NSDictionary class]]) {// 如果可以直接转换为 JSON 数据,则返回if ([NSJSONSerialization isValidJSONObject:model]) return model;NSMutableDictionary *newDic = [NSMutableDictionary new];// 遍历 model 的 key 和 value[((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;if (!stringKey) return;// 递归解析 value id jsonObj = ModelToJSONObjectRecursive(obj);if (!jsonObj) jsonObj = (id)kCFNull;newDic[stringKey] = jsonObj;}];return newDic;}// 如果 model 从属于 NSSetif ([model isKindOfClass:[NSSet class]]) {// 如果能够直接转换 JSON 对象,则直接返回// 否则遍历,按需要递归解析...}if ([model isKindOfClass:[NSArray class]]) {// 如果能够直接转换 JSON 对象,则直接返回// 否则遍历,按需要递归解析...}// 对 NSURL, NSAttributedString, NSDate, NSData 做相应处理if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];if ([model isKindOfClass:[NSData class]]) return nil;// 用 [model class] 初始化一个模型元_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];// 如果映射表为空,则不做解析直接返回 nilif (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;// 性能优化细节,使用 __unsafe_unretained 来避免在下面遍历 block 中直接使用 result 指针造成的不必要 retain 与 release 开销NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];__unsafe_unretained NSMutableDictionary *dic = result; // 遍历模型元属性映射字典[modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {// 如果遍历当前属性元没有 getter 方法,跳过if (!propertyMeta->_getter) return;id value = nil;// 如果属性元属于 CNumber,即其 type 是 int、float、double 之类的if (propertyMeta->_isCNumber) {// 从属性中利用 getter 方法得到对应的值value = ModelCreateNumberFromProperty(model, propertyMeta);} else if (propertyMeta->_nsType) { // 属性元属于 nsType,即 NSString 之类// 利用 getter 方法拿到 valueid v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);// 对拿到的 value 递归解析value = ModelToJSONObjectRecursive(v);} else {// 根据属性元的 type 做相应处理switch (propertyMeta->_type & YYEncodingTypeMask) {// id,需要递归解析,如果解析失败则返回 nilcase YYEncodingTypeObject: {id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);value = ModelToJSONObjectRecursive(v);if (value == (id)kCFNull) value = nil;} break;// Class,转 NSString,返回 Class 名称case YYEncodingTypeClass: {Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);value = v ? NSStringFromClass(v) : nil;} break;// SEL,转 NSString,返回给定 SEL 的字符串表现形式case YYEncodingTypeSEL: {SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);value = v ? NSStringFromSelector(v) : nil;} break;default: break;}}// 如果 value 还是没能解析,则跳过if (!value) return;// 当前属性元是 KeyPath 映射,即 a.b.c 之类if (propertyMeta->_mappedToKeyPath) {NSMutableDictionary *superDic = dic;NSMutableDictionary *subDic = nil;// _mappedToKeyPath 是 a.b.c 根据 '.' 拆分成的字符串数组,遍历 _mappedToKeyPathfor (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {NSString *key = propertyMeta->_mappedToKeyPath[i];// 遍历到结尾if (i + 1 == max) {// 如果结尾的 key 为 nil,则使用 value 赋值if (!superDic[key]) superDic[key] = value;break;}// 用 subDic 拿到当前 key 对应的值subDic = superDic[key];// 如果 subDic 存在if (subDic) {// 如果 subDic 从属于 NSDictionaryif ([subDic isKindOfClass:[NSDictionary class]]) {// 将 subDic 的 mutable 版本赋值给 superDic[key]subDic = subDic.mutableCopy;superDic[key] = subDic;} else {break;}} else {// 将 NSMutableDictionary 赋值给 superDic[key]// 注意这里使用 subDic 间接赋值是有原因的,原因就在下面subDic = [NSMutableDictionary new];superDic[key] = subDic;}// superDic 指向 subDic,这样在遍历 _mappedToKeyPath 时即可逐层解析// 这就是上面先把 subDic 转为 NSMutableDictionary 的原因superDic = subDic;subDic = nil;}} else {// 如果不是 KeyPath 则检测 dic[propertyMeta->_mappedToKey],如果为 nil 则赋值 valueif (!dic[propertyMeta->_mappedToKey]) {dic[propertyMeta->_mappedToKey] = value;}}}]; // 忽略,对应 modelCustomTransformToDictionary 接口if (modelMeta->_hasCustomTransformToDictionary) {// 用于在默认的 Model 转 JSON 过程不适合当前 Model 类型时提供自定义额外过程// 也可以用这个方法来验证转换结果BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];if (!suc) return nil;}return result;
}
代码还是有些长,这里笔者简单讲一下这里的内容,简单总结一下上面的代码逻辑:
- 判断入参,如果满足条件可以直接返回
- 如果Model从属于NSType,根据不同的类型做逻辑处理
- 如果上面条件不被满足,则用Model的Class初始化一个模型元
_YYModelMeta
- 判断模型元的映射关系,遍历映射拿到对应的键值存入字典中返回
这里有一个性能优化的细节,用__unsafe_unretained
修饰的dic指向我们最后要return的NSMutableDictionary *result
.
这里作者大篇幅的采用来unsafe_unretained
来处理循环引用的内容,这里就可以减少很多不需要的retain
和release
的操作,因为这样可以提高速度,
总结
这里笔者学习YYModel主要是为了认识为什么YYModel有更多的性能优势?
- 首先YYModel十分轻量级,它将Runtime的底层结构体封装了成了方便调用的结构体,减少来代码逻辑
- 同时YYModel中频繁调用了Core Foundation框架中的内容,
Core Foundation
方法在调用的时候远远优与Foundation
中的方法 - 同时在YYModel中经常使用纯C函数,避免了消息发送的开销
- 还有与JSONModel最有区别的一点就是YYModel中使用
setter
方法进行了value值的设置而非KVC
,这样也提高了性能 - YYModel采用了分类,实现了对于Model的无入侵性,这点比较重要
相关文章:

【iOS】YYModel源码解析
YYModel源码解析 文章目录 YYModel源码解析前言YYModel性能优势YYModel简介YYClassInfo解析YYClassIvarInfo && objc_ivarYYClassMethodInfo && objc_methodYYClassPropertyInfo && property_tYYClassInfo && objc_class YYClassInfo的初始化细…...

C++算法训练营 Day6 哈希表(1)
1.有效的字母异位词 LeetCode:242.有效的字母异位词 给定两个字符串s和t ,编写一个函数来判断t是否是s的字母异位词。 示例 1: 输入: s “anagram”, t “nagaram” 输出: true 示例 2: 输入: s “rat”, t “car” 输出: false 解题思路ÿ…...

【C语言编译与链接】--翻译环境和运行环境,预处理,编译,汇编,链接
目录 一.翻译环境和运行环境 二.翻译环境 2.1--预处理(预编译) 2.2--编译 2.2.1--词法分析 2.2.2--语法分析 2.2.3--语义分析 2.3--汇编 2.4--链接 三.运行环境 🔥个人主页:草莓熊Lotso的个人主页 🎬作者简介:C研发…...

【JavaEE】多线程
8.线程状态 根据 Java 的Thread.state包,线程一共有六种状态: NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED 二、每种状态的含义 1. NEW(新建) 当使用new 关键字创建一个线程对象,但尚未调用其start() 方法时…...

【项目】在线OJ(负载均衡式)
目录 一、项目目标 二、开发环境 1.技术栈 2.开发环境 三、项目树 目录结构 功能逻辑 编写思路 四、编码 1.complie_server 服务功能 代码蓝图 开发编译功能 日志功能 编辑 测试编译模块 开发运行功能 设置运行限制 jsoncpp 编写CR 如何生成唯一文件名 …...

贪心算法应用:在线租赁问题详解
贪心算法应用:在线租赁问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的算法策略。在线租赁问题(Greedy Algorithm for Online Rentals)是一个经典的贪心算法应用场景,下面我将从多个维度全面…...
torch.zeros()用法简介
torch.zeros()是PyTorch中用于创建全零张量的核心函数,其功能和使用方法如下: 1. 基本语法 torch.zeros(*size, outNone, dtypeNone, layouttorch.strided, deviceNone, requires_gradFalse)参数说明: *size:定义张量形状的…...

Prj10--8088单板机C语言8259测试(1)
1.原理图 2.Deepseek示例代码 #include <dos.h> #include <conio.h> #include <stdio.h>#define PIC1_CMD 0x400 // 命令端口 (A00) #define PIC1_DATA 0x401 // 数据端口 (A01)volatile int int_count 0; // 中断计数器 void interrupt (*old_isr)(…...

3步在小米13手机跑DeepSeek R1
大家好!我是羊仔,专注AI工具、智能体、编程。 一、从性能旗舰到AI主机 春节大扫除时,翻出尘封的小米13,这台曾以骁龙8 Gen2著称的性能小钢炮,如今正在执行更科幻的使命——本地运行DeepSeek R1。 想起两年前用它连续肝…...
数智管理学(十六)
二、分布式网络型结构的特点 分布式网络型结构是一种去中心化、扁平化和协作性的组织模式,与传统金字塔型结构形成鲜明对比。它通过赋予团队和个体更大的自主权,提升组织的灵活性和响应能力。 (一)节点化组织 1.模块化团队构成…...

注销微软账户
因为我的微软开发者账户丢失 Office E5 权限,因此需要注销。 若你需要注销微软账号,请点击下方超链接。 点击此处 注销之后仅剩一个正常的账户使用咯!!...

Ubuntu 服务器软件更新,以及常用软件安装 —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录 3
前言 前面,我们已经 安装好了 Ubuntu 服务器系统,并且 配置好了 ssh 免密登录服务器 ,现在,我们要来进一步的设置服务器。 那么,本文,就是进行服务器的系统更新,以及常用软件的安装 调整 Ubu…...
Mysql常用知识3:Kafka和数据库优化
文章目录 一、分布式消息系统(Kafka相关问题5-10)5. Kafka如何保证消息不丢失?6. 项目中Kafka具体怎么使用的?7. 消息异常未发送成功怎么解决?8. 重试具体怎么做的,循环吗?9. 重试多次失败怎么办…...
Milvus单机模式安装和试用
1.安装ollama的package包; # install package pip install -U langchain-ollama2.我们直接使用ChatOllama实例化模型,并通过invoke进行调用; from langchain_ollama import ChatOllamallm ChatOllama(model"deepseek-r1") messa…...

飞牛NAS+Docker技术搭建个人博客站:公网远程部署实战指南
文章目录 前言1. Docker下载源设置2. Docker下载WordPress3. Docker部署Mysql数据库4. WordPress 参数设置5. 飞牛云安装Cpolar工具6. 固定Cpolar公网地址7. 修改WordPress配置文件8. 公网域名访问WordPress总结 前言 在数字化浪潮中,传统网站搭建方式正面临前所未…...

刷leetcode hot100返航必胜版--链表6/3
链表初始知识 链表种类:单链表,双链表,循环链表 链表初始化 struct ListNode{ int val; ListNode* next; ListNode(int x): val(x),next(nullptr) {} }; //初始化 ListNode* head new ListNode(5); 删除节点、添加…...

C# 序列化技术全面解析:原理、实现与应用场景
在软件开发中,数据持久化和网络通信是两个至关重要的环节。想象一下,当我们需要将一个复杂的对象保存到文件中,或者通过网络发送到另一台计算机时,如何有效地表示这个对象?这就是序列化技术要解决的问题。序列化&#…...
isp调试 blend模式指什么
isp调试 blend模式指什么 答案摘自豆包: 在图像信号处理(ISP,Image Signal Processor)调试中,Blend 模式(混合模式) 是指将不同处理阶段的图像数据或不同来源的图像信息按照特定规则进行叠加或…...

electron定时任务,打印内存占用情况
// 监听更新 function winUpdate(){// 每次执行完后重新设置定时器try {// 获取当前时间并格式化为易读的字符串const now new Date();const timeString now.toLocaleString();console.log(当前时间: ${timeString});// 记录内存使用情况(可选)const m…...

Gitee Wiki:以知识管理赋能 DevSecOps,推动关键领域软件自主演进
关键领域软件研发中的知识管理困境 传统文档管理模式问题显著 关键领域软件研发领域,传统文档管理模式问题显著:文档存储无系统,查找困难,降低效率;更新不及时,与实际脱节,误导开发࿱…...

学习STC51单片机24(芯片为STC89C52RCRC)
每日一言 把 “我不行” 换成 “我试试”,你会发现一片新的天地。 那关于优化 白盒测试 我们之前不是通过这个接线方式可以看到返回到信息嘛因为安信可的特性就是返回Esp8266的反馈,可以看到代码死在哪里了,导致连接不上,因为我们…...

LabVIEW基于 DataSocket从 OPC 服务器读取数据
LabVIEW 中基于 DataSocket 函数从 OPC 服务器读取数据的功能,为工业自动化等场景下的数据交互提供了解决方案。通过特定函数实现 URL 指定、连接建立与管理、数据读取,相比传统 Socket 通信和 RESTful API ,在 OPC 服务器数据交互场景有适配…...

阿里云无影云桌面深度测评
阿里云无影桌面深度测评:解锁云端工作“新范式”的“未来之钥”! 在数字化浪潮席卷全球的2025年,远程办公与混合办公已不再是权宜之计,而是职场不可逆转的新常态。然而,如何确保员工无论身在何处,都能拥有…...
【208】VS2022 C++ 32位整数和unsigned char数组之间互相转换
一、场景 在实际应用中,特别是在数据传输的时候,需要读取unsigned char数组,再转换成 32 位整数;或者把 32 位整数转换成 unsigned char数组进行写入。比如对接西门子PLC的 snap7 就是这样。32 位整数分成有符号的无符号的&#…...
数据库技术
InnoDB是什么?MySQL 和 InnoDB的关系是什么? InnoDB是MySQL数据库系统中最重要且默认的存储引擎。MySQL采用插件式存储引擎架构,作为数据库管理系统本身不直接处理数据存储,而是通过存储引擎接口与InnoDB等引擎交互。InnoDB作为M…...

深入浅出:Oracle 数据库 SQL 执行计划查看详解(1)——基础概念与查看方式
背景 在当今的软件开发领域,尽管主流开发模式往往倾向于采用单表模式,力图尽可能地减少表之间的连接操作,以期达到提高数据处理效率、简化应用逻辑等目的。然而,对于那些已经上线运行多年的运维老系统而言,它们内部往…...

前端HTML contenteditable 属性使用指南
什么是 contenteditable? HTML5 提供的全局属性,使元素内容可编辑类似于简易富文本编辑器兼容性 支持所有现代浏览器(Chrome、Firefox、Safari、Edge) 移动端(iOS/Android)部分键盘行为需测试 &l…...

自动化采集脚本与隧道IP防封设计
最近群里讨论问如何编写一个自动化采集脚本,要求使用隧道IP(代理IP池)来防止IP被封。这样的脚本通常用于爬虫或数据采集任务,其中目标网站可能会因为频繁的请求而封禁IP。对于这些我还是有些经验的。 核心思路: 1、使…...

【设计模式-4.7】行为型——备忘录模式
说明:本文介绍行为型设计模式之一的备忘录模式 定义 备忘录模式(Memento Pattern)又叫作快照模式(Snapshot Pattern)或令牌模式(Token Pattern)指在不破坏封装的前提下,捕获一个对…...

docker离线镜像下载
背景介绍 在某些网络受限的环境中,直接从Docker Hub或其他在线仓库拉取镜像可能会遇到困难。为了在这种情况下也能顺利使用Docker镜像,我们可以提前下载好所需的镜像,并通过离线方式分发和使用。 当前镜像有:python-3.8-slim.ta…...