先看一道題:
#import "RootVC.h"
@interface NSObject (myTest)
+ (void) testFunc;
@end
@implementation NSObject (myTest)
-(void) testFunc
{
NSLog(@"testFunc: 執(zhí)行");
}
@end
@implementation RootVC
- (void)viewDidLoad
{
[super viewDidLoad];
[NSObject testFunc];
NSObject *obj = [[NSObject alloc]init];
[obj testFunc];
}
@end
程序執(zhí)行的結(jié)果是什么?為什么呢?
如果要解答這道題,就需要對(duì)OC的方法調(diào)用過程非常了解才能答對(duì)。
先看看運(yùn)行結(jié)果:
2017-08-07 15:38:14.835648+0800 testruntime[12184:1830852] testFunc: 執(zhí)行
2017-08-07 15:38:14.835712+0800 testruntime[12184:1830852] testFunc: 執(zhí)行
類在Runtime中的結(jié)構(gòu)
struct objc_class
{
Class isa OBJC_ISA_AVAILABILITY;
//isa指針,這是個(gè)啥?,表示是一個(gè)什么,
//實(shí)例的isa指向類對(duì)象,類對(duì)象的isa指向元類
#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;
//方法緩存列表
//調(diào)用過的方法存入緩存列表,下次調(diào)用先找緩存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
//協(xié)議列表
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
元類MetaClass
所有的類自身也是一個(gè)對(duì)象,可以向這個(gè)對(duì)象發(fā)送消息(即調(diào)用類方法)。
NSArray *array = [NSArray array];
+array消息發(fā)送給了NSArray類,而這個(gè)NSArray也是一個(gè)對(duì)象。
既然是對(duì)象,那么它也是一個(gè)objc_object指針,包含一個(gè)指向其類的一個(gè)isa指針。為了調(diào)用+array方法,這個(gè)類的isa指針必須指向一個(gè)包含這些類方法的一個(gè)objc_class結(jié)構(gòu)體。這就需要meta_class概念。
meta_class是一個(gè)類對(duì)象的類。當(dāng)我們向一個(gè)對(duì)象發(fā)送消息時(shí),Runtime會(huì)在這個(gè)對(duì)象所屬的這個(gè)類的方法列表中查找方法。而向一個(gè)類發(fā)送消息時(shí),會(huì)在這個(gè)類的meta_class的方法列表中查找。meta_class很重要,因?yàn)樗鎯?chǔ)著一個(gè)類的所有的類方法。每個(gè)類都會(huì)有一個(gè)單獨(dú)的meta_class,因?yàn)槊總€(gè)類的類方法基本不可能完全相同。
方法調(diào)用的過程
調(diào)用方法分為調(diào)用實(shí)例方法和調(diào)用類方法,在結(jié)構(gòu)體我們可以看到第一個(gè)就是一個(gè) isa 指針,會(huì)指向類對(duì)象或者元類。
每個(gè)實(shí)例對(duì)象有個(gè)isa的指針,指向?qū)ο蟮念悾?br>
類里也有個(gè)isa的指針, 指向meteClass(元類)。
元類保存了類方法的列表。當(dāng)類方法被調(diào)用時(shí),先會(huì)從元類本身查找類方法的實(shí)現(xiàn),如果沒有,元類會(huì)向他父類查找該方法。同時(shí)注意的是:元類(meteClass)也是類,它也是對(duì)象。元類也有isa指針,它的isa指針最終指向的是一個(gè)根元類(root meteClass)。根元類的isa指針指向本身,這樣形成了一個(gè)封閉的內(nèi)循環(huán)。
方法調(diào)用的過程
1.在對(duì)象自己緩存的方法列表中去找要調(diào)用的方法,找到了就直接執(zhí)行其實(shí)現(xiàn)。
2.緩存里沒找到,就去上面說的它的方法列表里找,找到了就執(zhí)行其實(shí)現(xiàn)。
3.還沒找到,說明這個(gè)類自己沒有了,就會(huì)去向其父類里執(zhí)行1、2。
4.如果找到了根類還沒找到,那么就是沒有了,會(huì)轉(zhuǎn)向一個(gè)攔截調(diào)用的方法,我們可以自己在攔截調(diào)用方法里面做一些處理。
5.如果沒有在攔截調(diào)用里做處理,那么就會(huì)報(bào)錯(cuò)崩潰。

以上就是方法調(diào)用的過程。我們可以看到的是,所謂重寫父類方法,并不是抹除了父類方法,父類的方法還是存在的,只是我們?cè)谧宇惱锩嬲业搅司筒粫?huì)再去父類里找了,是這個(gè)層面的“覆蓋”。而我們熟悉的 super 調(diào)用父類的方法實(shí)現(xiàn),就是告知要去調(diào)用父類實(shí)現(xiàn)的標(biāo)識(shí)。
這樣回到開頭的題目,答案自然一目了然啊。
類方法去metaclass里面找,沒找到,去superclass也就是NSObject去找,找到了方法就去執(zhí)行。
實(shí)例方法直接找到就進(jìn)行執(zhí)行。