类方法和实例方法,类和元类

假设A类继承自B类,B类继承自NSObject
A便是途中的Subclass(class),B便是图中的Superclass(class),NSObject便是Root class(class);
A *a = [A new];
其实A和a一样,也是对象,A称为类对象,a称为实例对象。
每一个类对象都有一个isa指针,指向元类。
 

我们新建一个NSObject的分类NSObject+Method我们在里面实现两个方法

- (void)sayObject{

  NSLog(@"NSObject===%s",__func__);

}

+ (void)sayClass{

  NSLog(@"NSObject===%s",__func__);

}

新建一个AClass类继承于NSObject,我们直接调用如下代码

int main(int argc, const char * argv[]) {

  @autoreleasepool {

    AClass *p = [AClass alloc];

    [AClass performSelector:@selector(sayClass)];

    [AClass performSelector:@selector(sayObject)];


    NSLog(@"Hello, World!");

  }

  return 0;

}

2021-07-01 11:31:23.567714+0800 KCObjcBuild[70678:3948791] NSObject===+[NSObject(Method) sayClass]

2021-07-01 11:31:23.568099+0800 KCObjcBuild[70678:3948791] NSObject===-[NSObject(Method) sayObject]

可以看出来两个都调用成功了,在oc底层其实没有“+”“-”方法的区分,只是方法存储的位置不同而已。我们具体分析下调用成功的流程:

1、NSObject中sayClass是类方法,在NSObject的元类(Root Class Meta)中存储,AClass调用sayClass方法,会从AClass的元类中找,找不到的话。从元类的父类找(Super Class Meta),继续找根元类(Root Class Meta),此时找到imp,返回。

2、NSObject中sayObject的实例方法,在NSObject的类中存储,AClass调用sayObject,会从AClass的元类中找,找不到的话。从元类的父类找(Super Class Meta),继续找根元类(Root Class Meta),再继续找(Root Class class)找到NSObject,此时找到imp,返回。

我们再进一步,用实例对象p调用看看是怎样的结果

int main(int argc, const char * argv[]) {

  @autoreleasepool {

    AClass *p = [AClass alloc];

    //[p performSelector:@selector(sayClass)];

    [p performSelector:@selector(sayObject)];

    NSLog(@"Hello, World!");

  }

  return 0;

}

我们发现sayObject调用成功了,sayClass直接崩溃,找不到方法

具体分析一下调用流程:

3、sayObject调用成功,我们很好理解。直接AClass继承于NSObject,AClass里面没有实现,直接找到父类的实现。

4、sayClass没有调用成功,我们前面分析过sayClass存在NSObject的元类(Root Class Meta)当中,对象去调用方法的时候,先查找AClass类中查找,此时没有,再往父类(NSObject)中查找,我们发现此时也没有。再找NSObject的父类就是nil了,所以查找不到。因为这个流程并没有查找到NSObject的元类(Root Class Meta)当中去,所以找不到。

总结实例对象可以调用NSObject里面的所有实例方法,但不能调用类方法,类可以掉用NSObject里面的所有方法,包括类方法和实例方法。

原因是类调用方法时,是从类的元类的方法列表里查找,一直往上找到元类的父类,一直到根元类,根元类的isa指向自己,NSObject的isa也指向它,从而找到NSObject的类方法列表,储存在根元类的方法列表中。

根元类的父类指向NSObject,因而找到NSObject方法列表。NSObject的方法列表中存储了NSObject的实例方法。

实例对象调用方法是先在自己的方法列表里找,然后父类,然后父类的父类,直到NSObject。NSObject的方法列表中存储了NSObject的实例方法。从而可以调用NSObject的实例方法。

NSObject的父类指向nil。所以无法找到NSObject的类方法列表,无法调用类方法。

有点绕,但只要看懂了上面的图形指向,就一目了然了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值