您好,登錄后才能下訂單哦!
YYModel
一個高性能模型框架。
作者在Github
上給出的性能對比圖(iphone 6 y:時間)
YYModel
:具體以下特點(diǎn):高性能、自動類型轉(zhuǎn)換、類型安全、非侵入性、輕量等。
關(guān)于如何使用YYModel
查看文檔和示例【傳送門】。
本文主要任務(wù),分析YYModel
的整體架構(gòu),實現(xiàn)思路,涉及到的知識點(diǎn)。
版本:1.0.4
YYModel
,只有5個文件。接下我們會具體看這五個文件都做了什么工作。
YYModel.h
頭文件,通過#import
該文件使用庫。YYClassInfo.h
根據(jù)名字應(yīng)該能猜出,關(guān)于Class信息的文件。NSObject + YYModel.h
這個NSObject
的一個Category
。還定義了一些內(nèi)部類。該文件只是一個頭文件,代碼很少。
#if __has_include(<YYModel/YYModel.h>)
FOUNDATION_EXPORT double YYModelVersionNumber;
FOUNDATION_EXPORT const unsigned char YYModelVersionString[];
#import <YYModel/NSObject+YYModel.h>
#import <YYModel/YYClassInfo.h>
#else
#import "NSObject+YYModel.h"
#import "YYClassInfo.h"
#endif
拓展:
FOUNDATION_EXPORT
是用來定義常量的,另外一個經(jīng)常用到的#define
定義常量。那么兩者的區(qū)別?
假設(shè)分別使用兩者定義字符串常量,前者可以通過==
來判斷字符串是否相等,后者則需要使用isEqualToString:
來判斷。因為,前者比較的是字符串指針地址,后者比較每個字符,因此前者效率更高。
__has_include()
#if __has_include(<UIKit/UIKit.h>) // 包含 #else // 不包含 #endif
判斷
UIKit
庫是否存在。
在YYClassInfo
文件中定義四個類,涉及到Runtime
知識,請看這篇文章博文或者直接查看[objc4源碼]()
YYClassIvarInfo
該類對應(yīng)實例變量信息(ivars),包含:名稱,偏移量,類型編碼,類型;其中類型請查看【官方文檔Type Encodings】
YYClassMethodInfo
方法的信息類,包含,方法名稱,SEL,IMP,參數(shù)類型編碼,返回值類型編碼等。
YYClassPropertyInfo
屬性信息類,包含名稱,類型,類型編碼,ivar名稱,類,協(xié)議列表,setter/getter等。
拓展
Ivar, Method, Property, SEL, IMP都是什么?
理解這些需要對
runtime
了解,上面給出了博文鏈接,這里簡單復(fù)習(xí)一下。
typedef struct objc_method *Method; // 方法
typedef struct objc_ivar *Ivar; // 實例變量
typedef struct objc_category *Category; //分類
typedef struct objc_property *objc_property_t; // 屬性
struct objc_class {
Class isa
Class super_class // 指向父類
const char * name // 名稱
long version
long info
long instance_size
struct objc_ivar_list * ivars // 實例變量表
struct objc_method_list * * methodLists //方法表
struct objc_cache * cache
struct objc_protocol_list * protocols //協(xié)議表
};
/* Use `Class` instead of `struct objc_class *` */
Ivar
指實例變量,存放在實例變量表中。Method
方法,存放在方法表中。
接下再看一下Objc_method
結(jié)構(gòu)體
struct objc_method {
SEL method_name
char * method_types
IMP method_imp
}
SEL
指方法名稱,IMP
指方法實現(xiàn)。
該文件定義三個分類和一個協(xié)議,以及兩個內(nèi)部類,下面是.h
文件中提供的接口。
提供了一些data
與model
轉(zhuǎn)換的方法。
// 根據(jù)接收到JSON創(chuàng)建一個實例,該方法是線程安全的。
// json對象可以是 NSDictionary,NSString,NSData.
+ (nullable instancetype)yy_modelWithJSON:(id)json;
// 字典轉(zhuǎn)Model
+ (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;
// 通過json設(shè)置屬性, 無效的數(shù)據(jù)會被忽略
- (BOOL)yy_modelSetWithJSON:(id)json;
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic;
// model轉(zhuǎn)json對象(NSDictionary/NSArray), NSData, NSString
- (nullable id)yy_modelToJSONObject;
- (nullable NSData *)yy_modelToJSONData;
- (nullable NSString *)yy_modelToJSONString;
#pragma mark - 其他快捷方法
// 拷貝(NSCoping協(xié)議)
- (nullable id)yy_modelCopy;
// 編碼和解碼(對應(yīng)NSCoding協(xié)議的兩個方法)
- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder;
- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder;
// NSObject協(xié)議
// 哈希值
- (NSUInteger)yy_modelHash;
// 相等判斷
- (BOOL)yy_modelIsEqual:(id)model;
// Debug描述
- (NSString *)yy_modelDescription;
以上NSObject分類中提供的接口,具體實現(xiàn)稍后學(xué)習(xí)。
// 直接添加以下代碼即可自動完成 - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } - (NSUInteger)hash { return [self yy_modelHash]; } - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } - (NSString *)description { return [self yy_modelDescription]; }
從json-array中創(chuàng)建一個數(shù)組, 其實也是遍歷循環(huán)調(diào)用字典轉(zhuǎn)Model。
+ (nullable NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json;
從json創(chuàng)建字典。
+ (nullable NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json;
YYModel
協(xié)議,通過實現(xiàn)響應(yīng)的方法,可以提供白名單,黑名單,自定義屬性名稱等功能。
// 自定義屬性名稱,可以將json中的名稱映射到自定義的名稱,可以解決沖突例如`id`。
+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
// 如果屬性是一個容器對象,例如NSArray/NSSet/NSDictonary,實現(xiàn)該方法可以返回一個映射字典(property -> class)
+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
// 白名單和黑名單(若實現(xiàn),忽略黑名單,只處理白名單)
+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
// 數(shù)據(jù)驗證和自定義轉(zhuǎn)換
// 當(dāng)JSON轉(zhuǎn)為Model完成后,會調(diào)用該方法??梢栽谠摲椒ㄖ羞M(jìn)行校驗工作,返回NO該Model被忽略,也可以完成一些轉(zhuǎn)換工作。
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
拓展
在源碼中會發(fā)現(xiàn)有一對
NS_ASSUME_NONNULL_BEGIN
和NS_ASSUME_NONNULL_END
宏。在Swift中存在
Option
類型,可以使用!
和?
聲明變量,但是在OC中沒有這個特性。出現(xiàn)新的關(guān)鍵詞用于OC轉(zhuǎn)Swift時區(qū)分能否為空。
nullable
&&nonnull
nullable
指對象可以為NULL。
nonnull
指對象不可以為NULL。如果不遵循這一規(guī)則,編譯器就會給出警告。
為了簡化書寫,在
NS_ASSUME_NONNULL_BEGIN
和NS_ASSUME_NONNULL_END
宏之間的代碼,默認(rèn)都是nonnull
。所以我們只需指定哪些nullable
的指針就可以了。
StackOverflow關(guān)于該宏的問題。
接下來補(bǔ)充一些知識點(diǎn),或許對以后開發(fā)有幫助。
NS_OPTIONS
&& NS_ENUM
這是兩個簡單方便的宏定義,從iOS6開始,他們?nèi)〈嗽瓉淼?code>enum。
例如:
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
其中第一個元素存儲類型。第二個參數(shù)是名字。
另外,enum
也可以被定義為按位掩碼。用簡單的OR和AND數(shù)學(xué)運(yùn)算既可實現(xiàn)一個整型值的編碼。請看這篇文章《NS_ OPTIONS && NS _ENUM - NShipster》。
本章主要整理YYModel
整體框架以及開發(fā)者提供的接口,并沒有涉及到內(nèi)部實現(xiàn)。接下來的文章,我將會一步步分析源碼實現(xiàn)。
Github - ibireme/YYModel
個人博客Owenli
微博Owenli_千
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。