iOS 方法调用的过程

先看一道题:

#import "RootVC.h"
@interface NSObject (myTest)
+ (void) testFunc;
@end

@implementation NSObject (myTest)
-(void) testFunc
{
    NSLog(@"testFunc: 执行");
}
@end

@implementation RootVC

- (void)viewDidLoad
{
    [super viewDidLoad];
    [NSObject testFunc];
    NSObject *obj = [[NSObject alloc]init];
    [obj testFunc];
}
@end

程序执行的结果是什么?为什么呢?
如果要解答这道题,就需要对OC的方法调用过程非常了解才能答对。
先看看运行结果:

2017-08-07 15:38:14.835648+0800 testruntime[12184:1830852] testFunc: 执行
2017-08-07 15:38:14.835712+0800 testruntime[12184:1830852] testFunc: 执行

类在Runtime中的结构

struct objc_class 
{
    Class isa  OBJC_ISA_AVAILABILITY;
    //isa指针,这是个啥?,表示是一个什么,
    //实例的isa指向类对象,类对象的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;
    //方法缓存列表
    //调用过的方法存入缓存列表,下次调用先找缓存

    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
   //协议列表

#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

元类MetaClass

所有的类自身也是一个对象,可以向这个对象发送消息(即调用类方法)。
NSArray *array = [NSArray array];
+array消息发送给了NSArray类,而这个NSArray也是一个对象。
既然是对象,那么它也是一个objc_object指针,包含一个指向其类的一个isa指针。为了调用+array方法,这个类的isa指针必须指向一个包含这些类方法的一个objc_class结构体。这就需要meta_class概念。
meta_class是一个类对象的类。当我们向一个对象发送消息时,Runtime会在这个对象所属的这个类的方法列表中查找方法。而向一个类发送消息时,会在这个类的meta_class的方法列表中查找。meta_class很重要,因为它存储着一个类的所有的类方法。每个类都会有一个单独的meta_class,因为每个类的类方法基本不可能完全相同。

方法调用的过程

调用方法分为调用实例方法和调用类方法,在结构体我们可以看到第一个就是一个 isa 指针,会指向类对象或者元类。

每个实例对象有个isa的指针,指向对象的类;
类里也有个isa的指针, 指向meteClass(元类)。
元类保存了类方法的列表。当类方法被调用时,先会从元类本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向本身,这样形成了一个封闭的内循环。

方法调用的过程
1.在对象自己缓存的方法列表中去找要调用的方法,找到了就直接执行其实现。
2.缓存里没找到,就去上面说的它的方法列表里找,找到了就执行其实现。
3.还没找到,说明这个类自己没有了,就会去向其父类里执行1、2。
4.如果找到了根类还没找到,那么就是没有了,会转向一个拦截调用的方法,我们可以自己在拦截调用方法里面做一些处理。
5.如果没有在拦截调用里做处理,那么就会报错崩溃。
iOS 方法调用的过程_第1张图片
方法调用过程.png

以上就是方法调用的过程。我们可以看到的是,所谓重写父类方法,并不是抹除了父类方法,父类的方法还是存在的,只是我们在子类里面找到了就不会再去父类里找了,是这个层面的“覆盖”。而我们熟悉的 super 调用父类的方法实现,就是告知要去调用父类实现的标识。

这样回到开头的题目,答案自然一目了然啊。
类方法去metaclass里面找,没找到,去superclass也就是NSObject去找,找到了方法就去执行。
实例方法直接找到就进行执行。

你可能感兴趣的:(iOS 方法调用的过程)