內(nèi)功修煉篇--runtime

前言

因?yàn)榍靶┤兆訉懥藗€(gè)關(guān)于導(dǎo)航欄控制器的Demo地址在這,開篇我想先稍微講一下這個(gè)琐旁,我是覺得原生的導(dǎo)航欄在UI如此豐富以及多層VC的情形下,導(dǎo)航條的顏色猜绣、按鈕灰殴、標(biāo)題、隱藏等定性的修改顯得不夠圓滑掰邢,因此就想采用一種透明的方式牺陶,將VC用NC包裝一層再push出去,這里就用到了AssociatedObjects尸变,為所推的VC添加了屬性义图。關(guān)聯(lián)對(duì)象只是運(yùn)行時(shí)中的一點(diǎn),本篇文章想就關(guān)聯(lián)對(duì)象和運(yùn)行時(shí)的一些其他常見用法姑且談?wù)勎嶂抟娬倮茫麙伌u引玉。

正文

其實(shí)網(wǎng)上關(guān)于運(yùn)行時(shí)的東西多如牛毛娃承,但感覺都像在一遍一遍的嚼舌根又不好理解奏夫,我就坦誠相見,拒絕抽象历筝。

#import <objc/runtime.h>

運(yùn)行時(shí)其實(shí)就是用C編寫的我們oc的基石酗昼,我們通過運(yùn)行時(shí)所提供的方法等可以跨越oc層直接與C交互,當(dāng)然對(duì)性能也會(huì)有所提升梳猪。運(yùn)行時(shí)會(huì)對(duì)一個(gè)類進(jìn)行完全的分解麻削,將類或者對(duì)象的每一個(gè)部分抽象成一種類型蒸痹,如果把oc的類比作一個(gè)組裝機(jī)器人,那他就會(huì)被運(yùn)行時(shí)拆分為手臂呛哟、腿叠荠、身體等,我們可以通過運(yùn)行時(shí)直接獲取到機(jī)器人的手臂一樣扫责,這對(duì)于操作一個(gè)類的屬性或者方法是非常方便的榛鼎。

我們在開發(fā)中切實(shí)可以用到的一些場景我做了歸納,下面一一講解:

1鳖孤、關(guān)聯(lián)對(duì)象

關(guān)聯(lián)對(duì)象相關(guān)的函數(shù)主要有三個(gè)者娱,命名相當(dāng)友好到一看就知道其實(shí)就是get/set方法,我們可以在category中使用它們實(shí)現(xiàn)動(dòng)態(tài)向類中添加屬性和方法苏揣。

  • objc_setAssociatedObject
  • objc_getAssociatedObject
  • objc_removeAssociatedObjects

看一個(gè)添加屬性的例子黄鳍,我們創(chuàng)建一個(gè)NSObject的分類CategoryProperty:

@interface NSObject (CategoryProperty)

@property (nonatomic, strong) NSObject *property;

@end

@implementation NSObject (CategoryProperty)

- (NSObject *)property {
return objc_getAssociatedObject(self, @selector(property));
}

- (void)setProperty:(NSObject *)value {
objc_setAssociatedObject(self, @selector(property), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
key值

這三個(gè)函數(shù)的參數(shù)key值推薦三種命名方式:

  • 聲明 static char kAssociatedObjectKey,使用 &kAssociatedObjectKey 作為 key 值;
  • 聲明 static void *kAssociatedObjectKey = &kAssociatedObjectKey 平匈,使用 kAssociatedObjectKey 作為 key 值框沟;
  • selector ,使用 getter 方法的名稱作為 key 值吐葱。

上面的例子用的是第三種方法街望,省的命名了也算簡單。

關(guān)聯(lián)策略

至于關(guān)聯(lián)策略有五種可供選擇弟跑,有強(qiáng)弱引用和原子非原子的區(qū)分灾前,在絕大多數(shù)情況下,我們都會(huì)使用OBJC_ASSOCIATION_RETAIN_NONATOMIC 的關(guān)聯(lián)策略孟辑,這可以保證我們持有關(guān)聯(lián)對(duì)象不會(huì)被過早的釋放哎甲。

在看一個(gè)添加方法的例子,我們創(chuàng)建一個(gè)UIButton的分類block:

typedef void (^btnBlock)();

@interface UIButton (block)

- (void)handelWithBlock:(btnBlock)block;

@end

static const char btnKey;

@implementation UIButton (block)

- (void)handelWithBlock:(btnBlock)block{
if (block){
    objc_setAssociatedObject(self, &btnKey, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
[self addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
}

- (void)btnAction{
btnBlock block = objc_getAssociatedObject(self, &btnKey);
block();
}

@end

這樣我們就為button添加了一個(gè)block的方法饲嗽,在調(diào)用button的時(shí)候就可以直接用handelWithBlock來回調(diào)了炭玫。

2、方法交換

顧名思義貌虾,就是兩個(gè)方法執(zhí)行交換吞加,我們建一個(gè)UIViewController的分類VCCategory:

@implementation UIViewController (VCCategory)

+ (void)load
{
//方法交換應(yīng)該被保證在程序中只會(huì)執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    SEL systemSel = @selector(viewWillAppear:);
    SEL henvySel = @selector(hl_viewWillAppear:);
    Method systemMethod = class_getInstanceMethod([self class], systemSel);
    Method henvyMethod = class_getInstanceMethod([self class], henvySel);
    BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(henvyMethod), method_getTypeEncoding(henvyMethod));
    if (isAdd) {
        //如果成功,說明類中不存在這個(gè)方法的實(shí)現(xiàn)
        //將被交換方法的實(shí)現(xiàn)替換到這個(gè)并不存在的實(shí)現(xiàn)
        class_replaceMethod(self, henvySel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
    }else{
        //否則尽狠,交換兩個(gè)方法的實(shí)現(xiàn)
        method_exchangeImplementations(systemMethod, henvyMethod);
    }
});
}
- (void)hl_viewWillAppear:(BOOL)animated{
//這里自己調(diào)用自己衔憨,表面上循環(huán)引用其實(shí)已經(jīng)被viewWillAppear替換掉了
[self hl_viewWillAppear:animated];
NSLog(@"henvy");
}
@end

主要看method_exchangeImplementations(systemMethod, henvyMethod),該函數(shù)將系統(tǒng)的方法和我的方法進(jìn)行了交換袄膏,這個(gè)時(shí)候在一個(gè)自己定義的viewController中viewWillAppear方法中就可以看到輸出henvy践图。這一點(diǎn)在我們需求需要更改的時(shí)候,但又不想改變原來的方法的時(shí)候是非常有用的沉馆。

3码党、發(fā)送消息

發(fā)送消息即objc_msgSend方法很簡單德崭,這里就舉個(gè)很簡單的例子,比如你要調(diào)用形如一下的一個(gè)方法,

//類揖盘、方法眉厨、參數(shù)
[someObject messageName:parameter];

還可以用objc_msgSend寫作為:

objc_msgSend(someObject,@selector(messageName),parameter);

4扣讼、字典轉(zhuǎn)模型

KVC是把字典中所有值給模型的屬性賦值缺猛,這個(gè)是要求字典中的Key必須要在模型里能找到相應(yīng)的值,如果找不到就會(huì)報(bào)錯(cuò)椭符,因此我們可以通過重寫KVC中的forUndefinedKey這個(gè)方法荔燎。當(dāng)然我們可以通過runtime的方式去實(shí)現(xiàn),類似于KVC逆操作销钝,先通過遍歷模型的值有咨,無誤后再賦值,這里新建一個(gè)模型ModelClass:

+ (instancetype)modelWithDict:(NSDictionary *)dict{

id objc = [[self alloc] init];
// count:成員變量個(gè)數(shù)
unsigned int count = 0;
// 獲取成員變量數(shù)組
Ivar *ivarList = class_copyIvarList(self, &count);

// 遍歷所有成員變量
for (int i = 0; i < count; i++) {
    // 獲取成員變量
    Ivar ivar = ivarList[i];
    
    // 獲取成員變量名字
    NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
    // 獲取成員變量類型
    NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
    // 格式化
    ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
    ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
    // 獲取key蒸健,序列為1是因?yàn)?_ivarName"
    NSString *key = [ivarName substringFromIndex:1];
    
    // 去字典中查找對(duì)應(yīng)value        
    id value = dict[key];
    
    // 判斷下value是否是字典
    if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
        // 獲取類
        Class modelClass = NSClassFromString(ivarType);
        value = [modelClass modelWithDict:value];
    }
    // 給模型中屬性賦值
    if (value) {
        [objc setValue:value forKey:key];
    }
}

return objc;
}

寫在最后

總體上來說運(yùn)行時(shí)在開發(fā)中比較常用的到的場景我就先總結(jié)這么多座享,當(dāng)然也歡迎大神能夠來補(bǔ)充是最好不過,文章中的測試代碼我都寫在前言的Demo里了似忧,同時(shí)也歡迎到我的Github討論渣叛,如果本文有什么不太對(duì)的地方,也請一定要給我指正盯捌,感激不盡淳衙!

好了寫了這么多有點(diǎn)累洗洗睡了,祝大家晚安饺著!另外送上一首歌【李志—墻上的向日葵】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末箫攀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子幼衰,更是在濱河造成了極大的恐慌靴跛,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渡嚣,死亡現(xiàn)場離奇詭異梢睛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)识椰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門扬绪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人裤唠,你說我怎么就攤上這事∮。” “怎么了种蘸?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵墓赴,是天一觀的道長。 經(jīng)常有香客問我航瞭,道長诫硕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任刊侯,我火速辦了婚禮章办,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滨彻。我一直安慰自己藕届,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布亭饵。 她就那樣靜靜地躺著休偶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辜羊。 梳的紋絲不亂的頭發(fā)上踏兜,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音八秃,去河邊找鬼碱妆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛昔驱,可吹牛的內(nèi)容都是我干的疹尾。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼舍悯,長吁一口氣:“原來是場噩夢啊……” “哼航棱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起萌衬,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤饮醇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后秕豫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朴艰,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年混移,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祠墅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歌径,死狀恐怖毁嗦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情回铛,我是刑警寧澤狗准,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布克锣,位于F島的核電站,受9級(jí)特大地震影響腔长,放射性物質(zhì)發(fā)生泄漏袭祟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一捞附、第九天 我趴在偏房一處隱蔽的房頂上張望巾乳。 院中可真熱鬧,春花似錦鸟召、人聲如沸胆绊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辑舷。三九已至,卻和暖如春槽片,著一層夾襖步出監(jiān)牢的瞬間何缓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國打工还栓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碌廓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓剩盒,卻偏偏與公主長得像谷婆,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辽聊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉纪挎,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,681評(píng)論 0 9
  • 對(duì)于從事 iOS 開發(fā)人員來說,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,697評(píng)論 7 64
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,544評(píng)論 33 466
  • 對(duì)于一些網(wǎng)站跟匆,只需要把站點(diǎn)鏈接的最后一個(gè)“/”改為“%5c”异袄,就能夠暴露出網(wǎng)站的數(shù)據(jù)庫地址。為什么會(huì)出現(xiàn)這樣的奇怪...
    Safesonic閱讀 2,911評(píng)論 0 0
  • 2008年9月16日,一個(gè)農(nóng)村出來的學(xué)生迹冤,懷揣著對(duì)未來學(xué)習(xí)生涯的夢想讽营,踏上憧憬了很多年的大學(xué)路,記得那天艷陽高照泡徙,...
    璟燁工作室閱讀 202評(píng)論 0 0