各位iOS搬磚工毡熏,相信大家在面試的時候經(jīng)常會遇到Runtime相關(guān)的知識點(diǎn)坦敌,但是由于日常開發(fā)中的確很少用到,所以大家的回答也不盡人意痢法,今天就由我?guī)Т蠹姨剿饕环琑untime;
Runtime是什么:
我們都知道,將源代碼轉(zhuǎn)換為可執(zhí)行的程序杜顺,通常要經(jīng)過三個步驟:編譯财搁、鏈接、運(yùn)行躬络。不同的編譯語言尖奔,在這三個步驟中所進(jìn)行的操作又有些不同;
C語言作為一門靜態(tài)類語言,在它編譯的階段就已經(jīng)確定了所有變量的數(shù)據(jù)類型提茁,同時也確定好了要調(diào)用的函數(shù)淹禾,以及函數(shù)的實(shí)現(xiàn);
而Objective-C語言是一門動態(tài)語言茴扁。在編譯階段并不知道變量的具體數(shù)據(jù)類型铃岔,也不知道所真正調(diào)用的哪個函數(shù)。只有在運(yùn)行時間才檢查變量的數(shù)據(jù)類型峭火,同時在運(yùn)行時才會根據(jù)函數(shù)名查找要調(diào)用的具體函數(shù)毁习。這樣在程序沒運(yùn)行的時候,我們并不知道調(diào)用一個方法具體會發(fā)生什么卖丸。
Objective-C語言把一些決定性的工作從編譯階段纺且、鏈接階段推遲到了運(yùn)行時階段的機(jī)制,使得Objective-C變得更加靈活稍浆。我們甚至可以在程序運(yùn)行的時候载碌,動態(tài)的去修改一個方法的實(shí)現(xiàn)。而實(shí)現(xiàn)Objective-C語言運(yùn)行時機(jī)制的一切基礎(chǔ)就是Runtime衅枫。
Runtime其實(shí)有兩個版本:modern和legacy嫁艇。我們現(xiàn)在用的Object2.0采用的是現(xiàn)行(modern)版的Runtime系統(tǒng),只能運(yùn)行在iOS和macOS10.5之后的64位程序中为鳄。而macOS較老的32位程序扔采用Object 1中的(早期)legacy版本的Runtime系統(tǒng)裳仆。這兩個版本最大的區(qū)別在于當(dāng)你更改一個類的實(shí)例變量的布局時,在早期版本中你需要重新編譯他的子類孤钦,而現(xiàn)行版本就不需要歧斟。
Runtime基本是用C和匯編寫的,可見蘋果為了動態(tài)系統(tǒng)的高效而作出的努力偏形。蘋果和GNU(一個自由的操作系統(tǒng))各自維護(hù)一個開源的runtime版本静袖,這兩個版本之間都在努力的保持一致。
高級編程語言想要成為可執(zhí)行文件需要先編譯為匯編語言再匯編為機(jī)器語言俊扭,機(jī)器語言也是計(jì)算機(jī)能夠識別的唯一語言队橙,但是OC并不能直接編譯為匯編語言,而是要先轉(zhuǎn)寫為純C語言再進(jìn)行編譯和匯編的操作萨惑,從OC到C的過渡就是由runtime來實(shí)現(xiàn)的捐康。然后我們使用OC進(jìn)行面向?qū)ο箝_發(fā),而C語言更多的是面向過程開發(fā)庸蔼,這就需要將面向?qū)ο蟮念愞D(zhuǎn)變?yōu)槊嫦蜻^程的結(jié)構(gòu)體解总。
Runtime實(shí)際上是一個庫,這個庫使我們可以在程序運(yùn)行時動態(tài)的創(chuàng)建對象姐仅、檢查對象花枫、修改類和對象的方法刻盐。
了解Runtime,要先了解他們核心-消息傳遞(Messaging)
Runtime消息傳遞(Messaging):
Objective-C語言中劳翰,對象方法調(diào)用都是類似[receiver selector]敦锌;的形式,其本質(zhì)就是讓對象在運(yùn)行時發(fā)送消息的過程佳簸。
[receiver selector]
在編譯階段和運(yùn)行階段分別作了什么:
1.編譯階段:[ receiver selector] 方法被編譯器轉(zhuǎn)換為:
-objc_msgSend(receiver,selector)(不帶參數(shù))
-objc_msgSend(receiver,selector,org1,org2,…)(帶參數(shù))
2.運(yùn)行時階段:消息接受者receiver尋找對應(yīng)的selector乙墙。
-通過receiver的isa指針找到receiver的 Class(類);
-在Class(類)的cache(方法緩存)的散列表中尋找對應(yīng)的IMP(方法實(shí)現(xiàn))溺蕉;
-如果在cache中沒有找到對應(yīng)的IMP的話伶丐,就繼續(xù)在Class中的method list(方法列表)中找對應(yīng)的selector,如果找到疯特,填充到cache中哗魂,并且返回selector
-如果在Class類中沒有找到這個selector,就繼續(xù)在它的superClass中尋找;
-一旦找到對應(yīng)的selector漓雅,直接執(zhí)行receiver對應(yīng)selector方法實(shí)現(xiàn)的IMP录别。
-若找不到對應(yīng)的selector,消息被轉(zhuǎn)發(fā)或者臨時向receiver添加這個selector對應(yīng)的實(shí)現(xiàn)方法邻吞,否則就會發(fā)生崩潰组题。
相關(guān)應(yīng)用:黑魔法:方法添加和替換
添加方法:
//class_addMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
class_addMethod([self class],sel,(IMP)fooMethod,”v@:”);
替換方法:
@implementation ViewController
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewDidLoad);
SEL swizzledSelector = @selector(jkviewDidLoad);
Method originalMethod = class_getInstanceMethod(class,originalSelector);
Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
//judge the method named swizzledMethod is already existed.
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
// if swizzledMethod is already existed.
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)jkviewDidLoad {
NSLog(@"替換的方法");
[self jkviewDidLoad];
}
- (void)viewDidLoad {
NSLog(@"自帶的方法");
[super viewDidLoad];
}
@end
swizzling應(yīng)該只在+load中完成。在Objective-C的運(yùn)行時中抱冷,每個類有兩個方法都會自動調(diào)用崔列。+load是在一個類被初始裝載時調(diào)用,+initialize 是在應(yīng)用第一次調(diào)用該類的類方法或?qū)嵗椒ㄇ罢{(diào)用的旺遮。兩個方法都是可選的赵讯,并且只有在方法被實(shí)現(xiàn)的情況下才會被調(diào)用。
Swizzling應(yīng)該只在dispatch_once中完成耿眉,由于swizzling改變了全局的狀態(tài)边翼,所以我們需要確保沒個預(yù)防措施在運(yùn)行時都是可用的。原子操作就是這樣一個用于確保代碼只會被執(zhí)行一次的預(yù)防措施鸣剪,就算是在不同的線程中也能確保代碼只執(zhí)行一次组底。Grand Central Dispatch的dispatch_once滿足了所需要的需求,并且應(yīng)該被當(dāng)做使用swizzling的初始化單例方法的標(biāo)準(zhǔn)筐骇;
文章收錄自行走少年郎的
iOS 開發(fā):『Runtime』詳解(一)基礎(chǔ)知識
以及jackyshan的
iOS Runtime詳解