每個Objective-C對象都有一個隱藏的數據結構,這個數據結構是Objective-C對象的第一個成員變量,它就是isa指針。
在NSObject.h里面:
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
再點開 Class 的定義:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
}
這一些定義對於懂的人自然懂,不會的人根本看不懂。建議看完下面的例子,再點開參考里面的鏈接仔細看一遍。
現在我們知道的是,對於我們新建的一個類,都會有一個隱藏的屬性isa,可以通過它進行一些訪問。
我們現在新建一個類Parent,繼承於NSObject, 里面有成員方法-(void)selectorP,類方法+(void)ClassSelectorP。
再新建一個類Child,繼承於Parent,里面有成員方法-(void)selectorC, 類方法+(void)ClassSelectorC。
現在我們新建一個實例Child* child = [Chlid new];
1,當我們調用[child class] 的時候,child就會通過isa指針去找到Child的class。
2,當我們調用[child superclass]的時候,child 通過isa找到Child的class,再通過super_class,找到Parent的class。
在這里,再普及objc_class 的兩種類型:
class 實例對象(child、Child)的isa指向的結構體;
metaclass class的isa指向的一個結構體;
3,接着,調用[child SelectorC],child通過isa找到Child的class,在class(注意看上面 struct objc_class 的定義)的方法列表里面找到SelectorC;
4,再試着調用[child SelectorP],child通過isa找到Child的class,發現class里面並沒有這個方法,通過class里面的super_class找到Parent的class,在里面的方法列表找到了SelectorP;
5,再是類方法[Child ClassSelectorC],Child(請注意,大寫)通過isa找到Child的class,通過class的isa找到Child的metaclass,在metaclass的方法列表里面找到了ClassSelectorC;
6,再試着調用[Child ClassSelectorP],Child通過isa找到Child的class,通過class的isa找到Child的metaclass,發現metaclass里面並沒有這個方法,通過metaclass里面的super_class找到Parent的metaclass,在里面的方法列表找到了ClassSelectorP;
- (void)viewDidLoad {
[super viewDidLoad];
Class clazz = [self class];
Class clarr = [AroundMapController class];
Class metalclazz = objc_getMetaClass("AroundMapController");
if (class_respondsToSelector(metalclazz, @selector(viewWillAppear:))) {
NSLog(@"ok");
}
if (clarr == clazz) {
NSLog(@"2 ok");
}
}
這個是驗證runtime的機制,可以把這段代碼復制到自己的controller.m里面,運行一下。記得把AroundMapController改成自己的controller的名字,還有引入頭文件<objc/runtime.h>
這是幾個例子基本上已經涵蓋了大多數調用的情況。
細心的朋友可能已經發現,上面的例子中,child通過isa找到的類對象,其實就是Child 通過isa找到的class。(如果能理解這一點,基本上也算對objectC的isa機制也算入門)
最后為了理解class和metaclass的作用,大家可以換位思考一下,如果我們作為runtime的設計者,當開發者新建出來一個實例對象child的時候,我們應該存儲child的屬性和方法,同時又該如何響應其方法調用,最后還要記錄與Parent之間的繼承關系。
這時候,再來看看,這種圖。可以加深對isa機制的理解:
參考
http://www.cocoachina.com/ios/20141018/9960.html
http://blog.csdn.net/jasonblog/article/details/7246822
http://blog.csdn.net/totogo2010/article/details/8081253
--問答題---
如果給你一個child的實例對象,要如何才能訪問到它的父類的靜態方法ClassSelectorP?
