智勇博客:
最近比較有空,在這里分享一下OC的底層runtime進(jìn)行時的一些簡單運用,比較適合初學(xué)者來學(xué)習(xí)了解一下。
先說幾句干貨,讓大家了解一些 什么叫做runtime進(jìn)行時。
OC語言分4個階段:
1.編寫程序階段,也就是大家每天都在做的。
2.編譯階段
3.鏈接階段
4.運行階段,也就是runtime進(jìn)行時
OC雖然在編譯時,會進(jìn)行一次代碼檢測。但這緊緊只是檢查一些簡單的操作,比如有沒有寫錯什么關(guān)鍵字 哪里的語法有沒有寫錯,打個比喻就是像個老師檢查學(xué)生的作文中有沒有錯別字和病句一樣。如果有錯誤就會有errors或者warning信息提示出來,那都是編譯器檢查出來的。
進(jìn)行時就是代碼跑起來,被裝載到內(nèi)存中去了。而進(jìn)行時類型檢查就與前面講的編譯時類型檢查不一樣。不是簡單的掃描代碼。而是在內(nèi)存中做些操作,做些判斷。如果出現(xiàn)問題則直接崩潰。
這篇文章主要的是介紹runtime的入門級運用,下面來下實際點的。
runtime的使用場景其實可以有很多,只不過大家平常寫代碼時并沒有往這方面去想,自然就運用不上runtime了
比如需求是:
我們執(zhí)行兩段不一樣的代碼,但是執(zhí)行的方法名字需要一樣。某個時候調(diào)用這個方法 是執(zhí)行代碼段1 某個時候調(diào)用這個方法 就是執(zhí)行代碼段2。
還有一個就是:
在一個完整的項目中,突然項目經(jīng)理需要修改一個HUD彈框顯示的背景顏色和文字。而這個HUD又在整個項目中的無數(shù)個地方使用過。那么此時有一個很苦力很low的方法,就是整個項目中逐一修改。
還有很多案例等,我就不一一舉例了。
這些時候 其實都是可以用runtime來解決,就是幾句代碼的事而已。
下面介紹2個runtime中的方法:
method_exchangeImplementations 來交換2個方法中的IMP,
method_setImplementation 來直接設(shè)置某個方法的IMP
IMP可能某些同學(xué)會比較陌生一點,起始IMP可以看做為一個指針存儲器,OC中,調(diào)用某個函數(shù),轉(zhuǎn)換為C語言就是調(diào)用這個IMP指向的函數(shù) 說白一點,就是通過IMP來找到需要調(diào)用的函數(shù)!
理解了IMP 那么需要偷龍轉(zhuǎn)鳳 就簡單多了。只要把函數(shù)的IMP偷偷轉(zhuǎn)換掉,到時候系統(tǒng)調(diào)用的該函數(shù)的時候,其實就是調(diào)用了我們新傳進(jìn)去的IMP指向的函數(shù)了。
這里主要介紹一下method_exchangeImplementations 這個運行時方法,其他方法之后會陸續(xù)再寫另一篇文章來跟大家繼續(xù)講解。
下面直接上一段代碼:
+(void)load {
//創(chuàng)建一個類型 也可以直接寫成 [NSArray class]
Class targetClass = [NSClassFromString(@"NSArray") class];
//返回一個指定的函數(shù) 這個函數(shù)指向 [self override_lastObject] 這個函數(shù)
Method m1 = class_getInstanceMethod([self class], @selector(override_firstObject));
/**
* 給一個指定的類新增一個函數(shù)方法
*
* targetClass 需要新增函數(shù)的類
* override_lastObject 新增函數(shù)的名字
* imp 新增函數(shù)調(diào)用時 指向的地址
* char *types 字符串描述這個函數(shù)的參數(shù)和返回類型
*
*
* 返回BOOL值
*/
BOOL isbool = class_addMethod(targetClass, @selector(override_firstObject), method_getImplementation(m1), method_getTypeEncoding(m1));
if (isbool == YES) {
NSLog(@"添加方法成功");
} else {
NSLog(@"添加方法失敗");
}
//提取這個類targetClass 的這個override_firstObject函數(shù)方法
Method m2 = class_getInstanceMethod(targetClass, @selector(override_firstObject));
//提取這個類targetClass 的這個firstObject函數(shù)方法
Method m3 = class_getInstanceMethod(targetClass, @selector(firstObject));
//將兩個函數(shù)的Imp指向?qū)ο?對換
method_exchangeImplementations(m2, m3);
}
上面的代碼中 直接在+load方法中實現(xiàn) 整個程序運行的時候把代碼噻進(jìn)內(nèi)存中的時候就會調(diào)用一次。也就是程序剛開始運行的時候 我們就已經(jīng)把需要調(diào)換的方法 成功的調(diào)換了
之后只要有NSArray調(diào)用firstObject這個方法的時候
就會調(diào)用了我們自己新寫的方法override_firstObject。
相反也是,只要調(diào)用NSArray的override_firstObject方法時
就會調(diào)用原來的firstObject方法了。
需要注意的是 因為我們該的是整個NSArray類,所以會影響到整個程序的,并不是只影響這個控制器 或者 這個頁面的哦!所以使用的時候需要注意,別亂該系統(tǒng)級別的控件!
一下代碼是測試上面的方法 是否成功的:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array = @[@"1", @"2"];
NSString *str = [array firstObject];
NSLog(@"str = %@",str);
}
-(void)override_firstObject {
NSLog(@"調(diào)用自己新寫的firstObject方法");
[self override_firstObject];
}
上面看起來override_firstObject方法中 好像是一個遞歸死循環(huán)了,但其實不是,上面也說過了,override_firstObject 和 firstObject 兩個方法是做了Imp調(diào)換的。所以不會造成遞歸。
這里的控制臺輸出順序是:
2017-02-16 16:46:54.917 textDemo[4183:192569] 添加方法成功
2017-02-16 16:46:54.997 textDemo[4183:192569] 調(diào)用自己新寫的lastObject方法
2017-02-16 16:46:54.998 texDemo[4183:192569] str = 1
控制臺這樣的輸出 已經(jīng)證明了我們將兩個方法的Imp對換是成功的。
上面只是為了簡化代碼 所以隨便寫來證實method_exchangeImplementations方法的運用。
只要掌握了runtime的運用,在以后的敲代碼過程中 會有一個很大的幫助,能幫助大家走少很多彎路的喔!
感謝大家能看到完整篇文章! 在下一篇文章中,我將繼續(xù)分享一個我封裝的第三方。這個第三方里,主要功能都是依賴runtime的。
預(yù)告:下一篇文章將會在實戰(zhàn)項目中 運用到一下幾個runtime的方法,并且會有詳細(xì)的講解以下方法的作用 歡迎初學(xué)者來留意一下:
class_getInstanceMethod
class_addMethod
method_getImplementation
method_getTypeEncoding
method_exchangeImplementations
objc_setAssociatedObject
objc_getAssociatedObject