開(kāi)篇
關(guān)于Runtime,在平時(shí)的開(kāi)發(fā)中,我用得最多的就是方法混淆(Method Swizzling)和動(dòng)態(tài)添加屬性.雖然自己經(jīng)常用,但是很少去深究原理性的東西.今天就重溫一下費(fèi)腦的runtime;
使用場(chǎng)景
runtime的使用場(chǎng)景我總結(jié)一下,大致分為如下幾種,由淺入深.并且附上相關(guān)示例代碼:
- 1.給分類添加屬性
目前常用的兩種方式,一種是顯示定義定義屬性關(guān)聯(lián)的key,一種是直接通過(guò)方法名作為屬性關(guān)于的key.
顯示定義關(guān)聯(lián)的key

Paste_Image.png
** 直接通過(guò)方法名定義關(guān)聯(lián)的key**

隱試關(guān)聯(lián).png
- 2.方法混淆(Method Swizzling)
方法混淆在實(shí)際開(kāi)發(fā)中我就用到統(tǒng)計(jì)每個(gè)頁(yè)面進(jìn)入與退出的時(shí)候.其實(shí)完全把方法混洗的代碼抽出來(lái)成為一個(gè)工具.如下:

Swizzlling.png
這種寫(xiě)法稍微要嚴(yán)謹(jǐn)一點(diǎn)
說(shuō)一說(shuō)Method,SEL,IMP之間的關(guān)系,比較重要,我們直接來(lái)看,Method是runtime內(nèi)部定義的方法

Method定義.png
網(wǎng)上找了幾張圖,分析一下具體的實(shí)現(xiàn)過(guò)程

第一步.png

第二步.png
- 3.動(dòng)態(tài)添加方法(消息轉(zhuǎn)發(fā))
一般處理的就是當(dāng)類中沒(méi)有定義方法,而調(diào)用了會(huì)包方法找不到的錯(cuò)誤.可以通過(guò)消息轉(zhuǎn)發(fā)搞定這個(gè)異常.具體的消息轉(zhuǎn)發(fā)可以參考這里.來(lái)個(gè)圖吧.

消息轉(zhuǎn)發(fā)流程.png
.在實(shí)際應(yīng)用中一般就走到第一步就OK了.

實(shí)際應(yīng)用.png
- 4.字典轉(zhuǎn)模型
字典轉(zhuǎn)模型我一般比較少用,
這段代碼已經(jīng)用過(guò)好多次,josnModel里面已經(jīng)寫(xiě)爛了.我也只是一名搬運(yùn)工
+ (instancetype)modelWithDict:(NSDictionary *)dict
{
// 思路:遍歷模型中所有屬性-》使用運(yùn)行時(shí)
// 0.創(chuàng)建對(duì)應(yīng)的對(duì)象
id objc = [[self alloc] init];
// 1.利用runtime給對(duì)象中的成員屬性賦值
// class_copyIvarList:獲取類中的所有成員屬性
// Ivar:成員屬性的意思
// 第一個(gè)參數(shù):表示獲取哪個(gè)類中的成員屬性
// 第二個(gè)參數(shù):表示這個(gè)類有多少成員屬性,傳入一個(gè)Int變量地址,會(huì)自動(dòng)給這個(gè)變量賦值
// 返回值Ivar *:指的是一個(gè)ivar數(shù)組,會(huì)把所有成員屬性放在一個(gè)數(shù)組中,通過(guò)返回的數(shù)組就能全部獲取到。
/* 類似下面這種寫(xiě)法
Ivar ivar;
Ivar ivar1;
Ivar ivar2;
// 定義一個(gè)ivar的數(shù)組a
Ivar a[] = {ivar,ivar1,ivar2};
// 用一個(gè)Ivar *指針指向數(shù)組第一個(gè)元素
Ivar *ivarList = a;
// 根據(jù)指針訪問(wèn)數(shù)組第一個(gè)元素
ivarList[0];
*/
unsigned int count;
// 獲取類中的所有成員屬性
Ivar *ivarList = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
// 根據(jù)角標(biāo),從數(shù)組取出對(duì)應(yīng)的成員屬性
Ivar ivar = ivarList[i];
// 獲取成員屬性名
NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 處理成員屬性名->字典中的key
// 從第一個(gè)角標(biāo)開(kāi)始截取
NSString *key = [name substringFromIndex:1];
// 根據(jù)成員屬性名去字典中查找對(duì)應(yīng)的value
id value = dict[key];
// 二級(jí)轉(zhuǎn)換:如果字典中還有字典,也需要把對(duì)應(yīng)的字典轉(zhuǎn)換成模型
// 判斷下value是否是字典
if ([value isKindOfClass:[NSDictionary class]]) {
// 字典轉(zhuǎn)模型
// 獲取模型的類對(duì)象,調(diào)用modelWithDict
// 模型的類名已知,就是成員屬性的類型
// 獲取成員屬性類型
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// 生成的是這種@"@\"User\"" 類型 -》 @"User" 在OC字符串中 \" -> ",\是轉(zhuǎn)義的意思,不占用字符
// 裁剪類型字符串
NSRange range = [type rangeOfString:@"\""];
type = [type substringFromIndex:range.location + range.length];
range = [type rangeOfString:@"\""];
// 裁剪到哪個(gè)角標(biāo),不包括當(dāng)前角標(biāo)
type = [type substringToIndex:range.location];
// 根據(jù)字符串類名生成類對(duì)象
Class modelClass = NSClassFromString(type);
if (modelClass) { // 有對(duì)應(yīng)的模型才需要轉(zhuǎn)
// 把字典轉(zhuǎn)模型
value = [modelClass modelWithDict:value];
}
}
// 三級(jí)轉(zhuǎn)換:NSArray中也是字典,把數(shù)組中的字典轉(zhuǎn)換成模型.
// 判斷值是否是數(shù)組
if ([value isKindOfClass:[NSArray class]]) {
// 判斷對(duì)應(yīng)類有沒(méi)有實(shí)現(xiàn)字典數(shù)組轉(zhuǎn)模型數(shù)組的協(xié)議
if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
// 轉(zhuǎn)換成id類型,就能調(diào)用任何對(duì)象的方法
id idSelf = self;
// 獲取數(shù)組中字典對(duì)應(yīng)的模型
NSString *type = [idSelf arrayContainModelClass][key];
// 生成模型
Class classModel = NSClassFromString(type);
NSMutableArray *arrM = [NSMutableArray array];
// 遍歷字典數(shù)組,生成模型數(shù)組
for (NSDictionary *dict in value) {
// 字典轉(zhuǎn)模型
id model = [classModel modelWithDict:dict];
[arrM addObject:model];
}
// 把模型數(shù)組賦值給value
value = arrM;
}
}
if (value) { // 有值,才需要給模型的屬性賦值
// 利用KVC給模型中的屬性賦值
[objc setValue:value forKey:key];
}
}
return objc;
}
### 總結(jié)
這篇簡(jiǎn)單講了一下,runtime在開(kāi)發(fā)中用到的場(chǎng)景.屬于應(yīng)用層面,下篇開(kāi)始講原理
參考鏈接
[快速上手Runtime](http://www.itdecent.cn/p/e071206103a4)