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指针指向本身,这样形成了一个封闭的内循环。
通过isa,就可以不断往上方去回溯自己的父类等,而方法的调用也利用了这个过程:

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

方法调用过程.png

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

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值