标签:float 之间 back 注入 项目 cal 轻量级 nsstring oal
这段时间一直在忙新的需求,没有时间来整理代码,发表自己技术博客,今天我们来看一下YYModel的底层解析以及如何使用,希望对大家有所帮助!
YYModel是一个轻量级的JSON模型转换库,它的思路非常清晰代码风格也很好,所以还是建议大家看一下底层实现的逻辑,也可以从源码加深对Runtime的理解。
下面是YYModel第三方库的一些代码结构。
YYModel的总共文件只有5个文件
除掉YYModel.h之外,只剩下了YYClassInfo和NSObject+YYModel两个模块啦!
前面已经讲到YYClassInfo主要功能是将Runtime层级的结构体封装到NSObject层级以便调用。下面是YYClassInfo与Runtime层级对比:
下面是YYClassIvarInfo
/** Instance variable information. */ @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 /** 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
紧接着我们看一下Runtime的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 }
注:日常开发中,NSString类型的属性会用copy修饰,看上面YYClassIvarInfo中typeEncoding和name是用strong修饰。这是因为其内部先是通过Runtime方法拿到const char * 之后通过 stringWithUTF8String 方法之后转为 NSString 的。所以 NSString 这类属性在确定其不会在初始化之后出现被修改的情况下,使用 strong来修饰 做一次单纯的强引用在性能上是比 copy 要高的。
下面是YYClassMethodInfo
@interface YYClassMethodInfo : NSObject @property (nonatomic, assign, readonly) Method method; ///< 方法 @property (nonatomic, strong, readonly) NSString *name; ///< 方法名称 @property (nonatomic, assign, readonly) SEL sel; ///< 方法选择器 @property (nonatomic, assign, readonly) IMP imp; ///< 方法实现,指向实现方法函数的函数指针 @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< 方法参数和返回类型编码 @property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< 返回值类型编码 @property (nullable, nonatomic, strong, readonly) NSArray<nsstring *> *argumentTypeEncodings; ///< 参数类型编码数组 - (instancetype)initWithMethod:(Method)method; @end
YYClassMethodInfo则是对Rutime里面的objc_method的封装,紧接着我们看Runtime的objc_method结构体
struct objc_method { SEL _Nonnull method_name OBJC2_UNAVAILABLE; // 方法名称 char * _Nullable method_types OBJC2_UNAVAILABLE; // 方法类型 IMP _Nonnull method_imp OBJC2_UNAVAILABLE; // 方法实现(函数指针) }
YYClassPropertyInfo是对Runtime中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; @end</nsstring *>
然后来看一下Runtime的property_t结构体
struct property_t { const char *name; // 名称 const char *attributes; // 修饰 };
YYClassInfo封装了Runtime的objc_class,下面看一下YYClassInfo
@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
// 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;
注解:下面是Runtime关于class的知识
下面是对应的讲解。
+ (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 = info CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); dispatch_semaphore_signal(lock); } } return info; }
下面总结一下初始化主要步骤:
NSObject+YYModel在YYModel主要任务是利用YYClassInfo层级封装的类来执行JSON模型之间的转换逻辑。下面是NSObject+YYModel讲述的主要内容:
下面将部分讲解
NSObject+YYModel重新定义了两个类,来使用 YYClassInfo 中的封装。
@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
@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; // 忽略 ... } @end
#import <Foundation/Foundation.h> //"time":"2018-07-04 12:13:52", //"ftime":"2018-07-04 12:13:52", //"context":"快件已签收 签收人: 他人代收 感谢使用圆通速递,期待再次为您服 @interface IOALogisticsDetailModel : NSObject @property(nonatomic,copy)NSString *time; @property(nonatomic,copy)NSString *ftime; @property(nonatomic,copy)NSString *context; @end #import "IOALogisticsDetailModel.h" @implementation IOALogisticsDetailModel @end
下面是运用
- (NSArray <IOALogisticsDetailModel *>*)setupOrderWithArray:(NSArray <NSDictionary *>*)array{ NSMutableArray <IOALogisticsDetailModel *>*modelArray = [NSMutableArray arrayWithCapacity:array.count]; for(NSDictionary *dic in array){ IOALogisticsDetailModel *model = [IOALogisticsDetailModel yy_modelWithDictionary:dic]; if (!model) continue; [modelArray addObject:model]; } return modelArray; }
红色部分就是应用。
如果需要Model转为JSON如下
#import <Foundation/Foundation.h> @interface IOAOrderAftersaleRequestModel : NSObject @property(nonatomic,copy)NSString *order_sn; @property(nonatomic,copy)NSString *rec_id; @property(nonatomic,copy)NSString *is_type; @property(nonatomic,assign)NSInteger refund_count; @property(nonatomic,copy)NSString *content; @property(nonatomic,copy)NSString *return_attachs; @property(nonatomic,copy)NSString *shop_id; @property(nonatomic,copy)NSString *reason; @property(nonatomic,assign)float total; @end #import "IOAOrderAftersaleRequestModel.h" @implementation IOAOrderAftersaleRequestModel @end
下面是运用
//提交退货商品 @interface IOAOrderAftersaleRequest:IOARequest @property (nonatomic,strong)IOAOrderAftersaleRequestModel *requestModel; @end //提交退货商品 @implementation IOAOrderAftersaleRequest - (id)requestArgument{ NSMutableDictionary *dic = [IOAApiManager getParametersWithService:@"App.Order.SetOrderAftersaleGoodsrefundsList"]; NSDictionary *temDic = [self.requestModel yy_modelToJSONObject]; [dic addEntriesFromDictionary:temDic]; return dic; }
// JSON: { "n":"Harry Pottery", "p": 256, "ext" : { "desc" : "A book written by J.K.Rowing." }, "ID" : 100010 } // Model: @interface Book : NSObject @property NSString *name; @property NSInteger page; @property NSString *desc; @property NSString *bookID; @end @implementation Book //返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。 + (NSDictionary *)modelCustomPropertyMapper { return @{@"name" : @"n", @"page" : @"p", @"desc" : @"ext.desc", @"bookID" : @[@"id",@"ID",@"book_id"]}; }
// JSON { "author":{ "name":"J.K.Rowling", "birthday":"1965-07-31T00:00:00+0000" }, "name":"Harry Potter", "pages":256 } // Model: 什么都不用做,转换会自动完成 @interface Author : NSObject @property NSString *name; @property NSDate *birthday; @end @implementation Author @end @interface Book : NSObject @property NSString *name; @property NSUInteger pages; @property Author *author; //Book 包含 Author 属性 @end @implementation Book @end
下面在我们项目中的使用
#import <Foundation/Foundation.h> #import "IOAOrder.h" @interface IOAOrderGroup : NSObject @property(nonatomic,copy)NSString *order_id; @property(nonatomic,copy)NSString *parent_sn; @property(nonatomic,copy)NSString *order_sn; @property(nonatomic,copy)NSString *order_status; @property(nonatomic,copy)NSString *refund_status; @property(nonatomic,copy)NSString *return_status; @property(nonatomic,copy)NSString *pay_status; @property(nonatomic,copy)NSString *total_amount; @property(nonatomic,copy)NSString *company_name; @property(nonatomic,copy)NSString *company_logo; @property(nonatomic,copy)NSString *shop_id; @property(nonatomic,copy)NSString *stroe_id; @property(nonatomic,copy)NSString *order_amount; @property(nonatomic,assign)int store_id; @property (nonatomic,strong)NSArray<IOAOrder *> *goods_list; @end #import "IOAOrderGroup.h" #import <YYModel/YYModel.h> @implementation IOAOrderGroup + (NSDictionary *)modelContainerPropertyGenericClass{ return @{@"goods_list":[IOAOrder class]}; } @end #import <Foundation/Foundation.h> @interface IOAOrder : NSObject @property(nonatomic,copy)NSString *rec_id; @property(nonatomic,copy)NSString *order_id; @property(nonatomic,copy)NSString *brand_name; @property(nonatomic,copy)NSString *goods_id; @property(nonatomic,copy)NSString *goods_name; @property(nonatomic,copy)NSString *goods_sn; @property(nonatomic,copy)NSString *goods_num; @property(nonatomic,copy)NSString *market_price; @property(nonatomic,copy)NSString *goods_price; @property(nonatomic,copy)NSString *cost_price; @property(nonatomic,copy)NSString *member_goods_price; @property(nonatomic,copy)NSString *total_price; @property(nonatomic,copy)NSString *give_integral; @property(nonatomic,copy)NSString *spec_key; @property(nonatomic,copy)NSString *unit; @property(nonatomic,copy)NSString *spec_key_name; @property(nonatomic,copy)NSString *bar_code; @property(nonatomic,copy)NSString *is_comment; @property(nonatomic,copy)NSString *prom_type; @property(nonatomic,copy)NSString *prom_id; @property(nonatomic,copy)NSString *is_send; @property(nonatomic,copy)NSString *delivery_id; @property(nonatomic,copy)NSString *add_time; @property(nonatomic,copy)NSString *update_time; @property(nonatomic,copy)NSString *image_url; @property(nonatomic,assign)BOOL selected; @end #import "IOAOrder.h" @implementation IOAOrder @end
@interface User @property NSString *name; @property NSUInteger age; @end @implementation Attributes // 如果实现了该方法,则处理过程中会忽略该列表内的所有属性 + (NSArray *)modelPropertyBlacklist { return @[@"test1", @"test2"]; } // 如果实现了该方法,则处理过程中不会处理该列表外的属性。 + (NSArray *)modelPropertyWhitelist { return @[@"name"]; } @end
YYModel的核心是通过runtime获取结构体中得Ivars的值,将此值定义为key,然后给key赋value值,所以我们需要自己遍历容器(NSArray,NSSet,NSDictionary),获取每一个值,然后KVC。
标签:float 之间 back 注入 项目 cal 轻量级 nsstring oal
原文地址:https://www.cnblogs.com/guohai-stronger/p/9647269.html