runtime介紹:
runtime 簡稱運行時, 是一套純c編寫的API.
?objective-c是基于c的,為c添加了面向?qū)ο蟮奶匦?它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了runtime運行時來處理.可以說runtime是oc的幕后工作者.
我們都知道高級編程語言想要成為可執(zhí)行文件,需先編譯為匯編語言再匯編為機器語言,機器語言也是計算機能夠識別的唯一語言,但是oc并不能直接編譯為匯編語言,而是先要轉(zhuǎn)寫為純C語言在進行編譯和匯編操作,從oc到c語言的過渡就是就是由runtime實現(xiàn)的.
我們使用oc進行面向?qū)ο箝_發(fā),而c語言更多的是面向過程開發(fā),這就需要將面向?qū)ο蟮念愞D(zhuǎn)變?yōu)槊嫦蜻^程的的結構體.其實我們創(chuàng)建的一個對象或?qū)嵗褪且粋€結構體,這個結構體里定義了很多變量,有, 指向元類的isa, 指向父類的指針,類的名字,版本,實例大小,實例變量列表,方法列表,緩存,租售的協(xié)議列表,結構體如下:
structobjc_class {?
?????Class isa OBJC_ISA_AVAILABILITY;?
?????Class super_class
????const char * name
????long version?
????long info
????long instance_size
????struct objc_ivar_list *ivars
????struct objc_method_list **methodLists
????struct objc_cache *cache
????struct objc_protocol_list *protocols?
?}
runtime最主要的是消息機制.,
對于c語言,函數(shù)的調(diào)用在編譯的時候會決定調(diào)用哪個函數(shù).
oc的函數(shù)調(diào)用稱為消息發(fā)送,屬于動態(tài)調(diào)用過程.在編譯的時候并不能決定真正調(diào)用哪個函數(shù),只要聲明過就不會報錯,只有當運行的時候才會報錯,這是因為oc是運行時動態(tài)調(diào)用的.而c語言調(diào)用未實現(xiàn)的函數(shù)就會報錯.
runtime消息機制
NSMutableString *str = [[NSMutableString alloc] initWithString: @"hello"];
[str appendString:@" world"];
上述代碼的str稱為消息接受者,appendString:稱作選擇子,也就是我們常用的selector餐曹,selector和參數(shù)共同構成了消息橘券,所以第二句話可以理解為將消息:"增加一個字符串: is a good guy"發(fā)送給消息的接受者str。
objc_msgSend的工作原理, 為了匹配消息的接受者和選擇子首先會在消息的接受者所在的類中去搜索這個struct objc_cache,如果能找到就可以直接跳到相關的具體實現(xiàn)中去調(diào)用,如果找不到,再去 struct objc_method_list方法列表搜索,如果能找到就可以直接跳到相關的具體實現(xiàn)中去調(diào)用,如果找不到,就會通過super_class指針沿著繼承樹向上搜索,如果找到就跳轉(zhuǎn),如果到了繼承樹的根部(通常是NSObject)還沒找到,那就會包unrecongnized selector錯誤(其實在條用這個方法之前還會進行消息轉(zhuǎn)發(fā),還有三次機會處理, 這就體現(xiàn)了runtime的強大).
三次機會:
1,所屬類動態(tài)方法解析
如果沿繼承樹沒有搜索到相關方法,則會向接受者所屬類進行一次請求,看是否能動態(tài)添加一個方法,注意這是一個類方法,因為是是想接受者所屬的類進行請求
+(BOOL)resolveInstanceMethod:(SEL)name
2,備援接受者
當對象所屬類不能動態(tài)添加方法后,runtime就會詢問當前的接受者是否有其他對象可以處理這個位置的selector:
- (id)forwardingTargetForSelector:(SEL)aSelector;
3,消息重定向
當沒有北苑接受者事,就只剩下最后一次機會了,那就是消息重定向,這個時候runtime會將未知消息的所有細節(jié)都封裝為NSInvocation對象,方法如下:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
runtime的其他作用:?動態(tài)添加屬性,動態(tài)添加方法,交換方法(method swizzling)
獲取屬性
int main(int argc, const char * argv[]) {
????????@autoreleasepool {
????????????????Person* p = [[Person alloc] init];
????????????????p.cjmName = @"Jiaming Chen";
????????????????unsigned int propertyCount = 0;
????????????????objc_property_t *propertyList = class_copyPropertyList([p class], ????&propertyCount);
????????????????for (int i = 0; i < propertyCount; i++) {
????????????????????????const char* name = property_getName(propertyList[i]);
????????????????????????const char* attributes = property_getAttributes(propertyList[i]);
????????????????????????NSLog(@"%s %s", name, attributes);
????????????????}
????????????}
????return 0;
????} ?
動態(tài)添加屬性
class_addProperty
方法交換: 本質(zhì)是修改selector對應的_imp,也就是修改實例方法的具體實現(xiàn)
int main(int argc, const char * argv[]) {
????????????@autoreleasepool {
????????????????????Person *p = [[Person alloc] initWithName:@"Jiaming Chen" age:22];
????????????????????Method method1 = class_getInstanceMethod([p class], @selector(helloWorld));
????????????????????Method method2 = class_getInstanceMethod([p class], @selector(showMyself));
????????????????????method_exchangeImplementations(method1, method2);
????????????????????[p showMyself];
? ? ? ? ? ? ? ? ? ? ?[p helloWorld];
????????????????}
????????????????return 0;
????} ?
iOS runtime探究(一): 從runtime開始理解面向?qū)ο蟮念惖矫嫦蜻^程的結構體
iOS runtime探究(二): 從runtime開始深入理解OC消息轉(zhuǎn)發(fā)機制
iOS runtime探究(三): 從runtime開始理解OC的屬性property
iOS runtime探究(四): 從runtiem開始實踐Category添加屬性與黑魔法method swizzling