我們應(yīng)該經(jīng)常能碰到這樣的場(chǎng)景:在網(wǎng)絡(luò)數(shù)據(jù)回來之后需要將網(wǎng)絡(luò)數(shù)據(jù)轉(zhuǎn)化成實(shí)體(model),通常的做法是利用kvc來為實(shí)體賦值,或者利用現(xiàn)在已有的MJExtention、YYModel來做。
今天以我自己寫的HBEntity為例解析下如何利用runtime來為實(shí)體賦值。
HBEntity簡介:
HBEntity是一個(gè)利用runtime來將NSArray或NSDictionary對(duì)象轉(zhuǎn)化成自定義實(shí)體的工具,支持多實(shí)體嵌套、免除了MJExtention和YYModel中白名單和黑名單的概念,同時(shí)還支持key與屬性名之間的適配。
實(shí)現(xiàn)思路:
HBEntity實(shí)現(xiàn)將NSArray或NSDictionary對(duì)象轉(zhuǎn)化成自定義實(shí)體的思路比較簡單。我們把問題轉(zhuǎn)換一下,上學(xué)的時(shí)候我們經(jīng)常會(huì)遇到解y=f(x)的問題,現(xiàn)在把x與我們的NSArray和NSDictionary對(duì)象對(duì)應(yīng),y與我們自定義實(shí)體對(duì)應(yīng),f函數(shù)就是我們的HBEntity,我們來捋一下我們的已知條件,我們已知x和y,要求f! WTF,怎么解 。。 從x、y去推f,這個(gè)f可能有很多種?。?!我比較喜歡倒推。既然我們是要給自定義實(shí)體賦值,那我們需要知道這個(gè)實(shí)體有多少屬性,每個(gè)屬性是什么類型,有沒有自定義getter 和setter方法等。runtime在這個(gè)時(shí)候就派上用場(chǎng)了。
在拿到實(shí)體所有的屬性之后剩下的就是用NSDictionary或者NSArray對(duì)象(為了方便以下都簡稱為“對(duì)象”,我們自定義的類稱為實(shí)體)賦值了。所以在這里我也不需要去過濾對(duì)象中哪些值是我不想?yún)⑴c賦值的(也就是blacklist),對(duì)于我來說沒有blacklist和whitelist的區(qū)分。
總結(jié)起來為:
1.解析實(shí)體的屬性
2.用對(duì)象的值為實(shí)體的屬性賦值。
下面我們邊看代碼吧邊bb吧。
1.解析實(shí)體的屬性——runtime
解析實(shí)體的屬性時(shí)我們會(huì)用到 class_copyPropertyList方法,
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
該方法會(huì)將cls類里所有的屬性都放到一個(gè)數(shù)組里返回,outCount返回的是屬性的數(shù)量,如果該cls有父類,class_copyPropertyList方法并不會(huì)返回父類的屬性,當(dāng)然也不會(huì)將父類屬性的個(gè)數(shù)計(jì)算到outCount內(nèi)。這里需要說明的是objc_property_t是一個(gè)objc_property的結(jié)構(gòu)體指針,objc_property有name 和 attributes,如果我們聲明了一個(gè)屬性
@property (nonatomic,copy,readonly)NSString *test;
這里test就是objc_property的name,可以通過property_getName來獲取,而attributes可以通過property_getAttributes來獲取,attributes是我們聲明的屬性(nonatomic,copy,readonly等)的描述,我們先來看下一個(gè)測(cè)試Demo返回的attribues,再來蘋果文檔里是怎么描述這一塊的。

T@“NSString”,C,N,V_entityName這一串就是attributes
從上圖可以看出,每個(gè)attribute都有name和value兩部分組成,我們可以通過property_copyAttributeList來獲得某個(gè)屬性的atrributeList,每一項(xiàng)是objc_property_attribure_t類型,objc_property_attribure_t類型如下:
/// Defines a property attribute
typedef struct {
const char *name;? ? ? ? ? /**< The name of the attribute */
const char *value;? ? ? ? ? /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
那具體T、C、N這些符號(hào)代表的是什么意思呢?他的值又表示的是什么呢?我們求助一下官方文檔。

上面是官方文檔對(duì)T、C、N等做出的解答。下面再來看看他們的值代表的什么意思。

這里是官方文檔的部分截圖,可以看出我們可以通過T來知道該屬性的類型。
官方文檔里展示的例子并沒有包含所有的類型,在我的HBEntity里有補(bǔ)充(下載地址:https://github.com/knighthb/HBEntity),感興趣的同學(xué)可以下載下來看看。
基本上通過以上的幾個(gè)方法就能確定一個(gè)類里有多少屬性(不含父類的屬性)、每個(gè)屬性是詳細(xì)信息等。
2.利用對(duì)象為實(shí)體賦值
這里基本上就是鍵值對(duì)映射,如果在適配器里為key和屬性名做了適配,那么在賦值時(shí)將會(huì)把key對(duì)應(yīng)的value賦值給對(duì)應(yīng)的屬性。
對(duì)于普通的類(屬性里沒有自定義類、沒有數(shù)組等的類)這些工作已經(jīng)足夠了,對(duì)于比較棘手的我們就需要進(jìn)行一下處理,比如對(duì)于屬性是NSArray或NSMutableArray的類賦值時(shí)我們需要知道NSArray里裝的是什么,也許有同學(xué)會(huì)說,這個(gè)簡單,我們聲明屬性的時(shí)候聲明為NSArray這樣的形式就知道它是NSString的啦,乍一看是的,但是你用runtime根本拿不出來,因?yàn)檫@種寫法是編譯時(shí)的,只是告訴編譯器array里裝的是NSString,所以在runtime根本找不到。因此這塊避免不了MJExtention或YYModel里指定類名的這類操作,如果有同學(xué)有好的解決方法請(qǐng)不吝賜教。
另外還有一個(gè)棘手的問題就是數(shù)值類型的處理,需要裝箱拆箱過程,一直沒有想出出去硬編碼的方法,如果有同學(xué)解決過類似的問題也請(qǐng)不吝賜教。
最后上點(diǎn)Demo:
//HBTestPerson.h
#import "HBEntity.h"
#import "HBTestEntity.h"
@interface HBTestPerson : HBEntity
@property (nonatomic , copy)NSString * entityName;
@property (nonatomic , strong) NSString *entityNum;
@property (nonatomic , strong,setter=setEntityAge2:) HBTestEntity *testEntity;
@property (nonatomic , strong) NSMutableArray* testEntities;
@property (nonatomic , assign) BOOL boolTest;
@end
//HBTestPerson.m
#import "HBTestPerson.h"
@implementation HBTestPerson
- (NSDictionary *)hb_transferDic {
return @{@"entityname":@"entityName",
@"entitynum":@"entityNum"};
}
+ (NSDictionary *)hb_objectClassForKeyDic {
return @{@"testEntities":[HBTestEntity class]};
}
- (void)setEntityAge2:(HBTestEntity *)entityAge {
_testEntity = entityAge;
}
@end
//HBTestEntity.h
#import "HBEntity.h"
@interface HBTestEntity : HBEntity
@property (nonatomic , copy) NSString * name;
@property (nonatomic , copy) NSNumber * age;
@end
//HBTestEntity.m
#import "HBTestEntity.h"
@implementation HBTestEntity
- (void)setName:(NSString *)name {
_name = name;
}
@end
//HBArrayTestEntity.h
#import "HBEntity.h"
@interface HBArrayTestEntity : HBEntity
@property (nonatomic , strong) NSMutableArray * array;
@end
//HBArrayTestEntity.m
#import "HBArrayTestEntity.h"
#import "HBTestEntity.h"
@implementation HBArrayTestEntity
+ (NSDictionary *)hb_objectClassForKeyDic {
return @{@"array":[HBTestEntity class]};
}
@end
上面是幾個(gè)測(cè)試類,下面是測(cè)試代碼
NSDictionary * entityDic = @{@"entityname":@"hehe",
@"entitynum":@"1",
@"testEntity":@{@"name":@"xiaoming",
@"age":@(34)
},
@"testEntities":@[@{@"name":@"xiaoming",
@"age":@(34)
},
@{@"name":@"xiaoming",
@"age":@(34)
},
@{@"name":@"xiaoming",
@"age":@(34)
}]};
HBTestPerson * entity = [HBTestPerson transferEntityWithDic:entityDic];
NSArray * entityArray = @[@{@"name":@"xiaoming",
@"age":@(34)
},
@{@"name":@"xiaoming",
@"age":@(34)
},
@{@"name":@"xiaoming",
@"age":@(34)
}];
HBArrayTestEntity * arrayTestEntity = [HBArrayTestEntity transferEntityWithObject:entityArray];
結(jié)果如下:

總結(jié):
不要臉的取了個(gè)總結(jié),主要是為了貼下github的地址
github地址:https://github.com/knighthb/HBEntity
或者 git clone https://github.com/knighthb/HBEntity.git
也可以通過Pod 來安裝 HBEntity玩耍
pod 'HBEntity'
希望通過解析HBEntity能讓大家對(duì)runtime能有不一樣的認(rèn)識(shí),如果大家在玩耍的過程中遇到了問題或者發(fā)現(xiàn)了bug請(qǐng)及時(shí)聯(lián)系我
QQ:513179531
微信:knight_hb
或者在底下留言 ^ ^