小码哥iOS学习笔记第三天: isa和superclass

本文深入探讨了Objective-C中的对象模型,包括实例对象、类对象和元类对象,并详细解释了它们之间的关系以及如何通过isa和superclass指针查找方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • Objective-C中的对象, 主要可以分为3种

    • instance: 实例对象, 包含 isa和其他成员变量的值, ...
    • class: 类对象, 包含, isa、superclass、属性、对象方法、协议、成员变量的描述, ...
    • meta-class: 元类对象, 包含 isa、superclass、类方法, ...
  • 可以用下图表示每种对象中包含的内容

一、准备代码

  • 准备两个类, Person类继承自NSObject, Student类继承自Person, 具体如下:
@interface Person : NSObject <NSCopying> {
    int _age;
}
@property (nonatomic, assign) double height;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end

@implementation Person
- (void)personInstanceMethod {}
+ (void)personClassMethod {}

- (id)copyWithZone:(nullable NSZone *)zone {
    return nil;
}
@end

@interface Student : Person {
    int _no;
}
@property (nonatomic, assign) double weight;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end
@implementation Student
- (void)studentInstanceMethod {}
+ (void)studentClassMethod {}
@end
复制代码
  • Person类中, 主要包含以下内容

    • int类型的成员变量_age
    • double类型的属性height
    • 一个实例方法- (void)personInstanceMethod
    • 一个类方法+ (void)personClassMethod
    • 遵守NSCopying协议, 并实现- (id)copyWithZone:(nullable NSZone *)zone方法
  • Student类中, 主要包含以下内容

    • int类型的成员变量_no
    • double类型的成员变量weight
    • 一个对象方法- (void)studentInstanceMethod
    • 一个类方法+ (void)studentClassMethod

二、验证OC调用方法是发送消息机制

  • OC中方法的调用, 是通过发送消息机制实现的, 我们可以查看底层代码来验证

  • 创建Person类的对象, 调用方法personInstanceMethod方法

Person *person = [[Person alloc] init];
[person personInstanceMethod];
复制代码
  • 当前的代码如下图:

  • 我们使用终端cdmain.m的文件中, 并执行命令:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
复制代码
  • 将生成的main-arm64.cpp文件拖到当前工程中:

  • 可以在main-arm64.cpp文件中, 找到如下代码:

  • 可以看到有一句代码:
 ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("personInstanceMethod"));
复制代码
  • 去掉类型转换后:
objc_msgSend(person, sel_registerName("personInstanceMethod"));
复制代码
  • 即: OC中调用方法, 在底层就是发送一条消息

三、对象的isa指针是指向哪里?

问: 对象的isa指针是指向哪里?
答: instance的isa, 指向class类对象, 类对象的isa指向meta-class元类对象

  • NSObject对象为例

    • NSObject的实例对象: isa 指向 NSObjectclass类对象
    • NSObject类对象: isa 指向 NSObjectmeta-class元类对象
    • NSObjectmeta-class元类对象: isa指向 NSObjectmeta-class元类对象
  • Person对象为例

    • Person的实例对象: isa 指向 Personclass类对象
    • Person类对象: isa 指向 Personmeta-class元类对象
    • Personmeta-class元类对象: isa指向 NSObjectmeta-class元类对象
  • Student对象为例

    • Student的实例对象: isa 指向 Studentclass类对象
    • Student类对象: isa 指向 Studentmeta-class元类对象
    • Studentmeta-class元类对象: isa指向 NSObjectmeta-class元类对象
  • instanceisa指向class

    • 当调用对象方法时, 通过instanceisa找到class, 最后找到对象方法的实现进行调用
  • classisa指向meta-class

    • 当调用类方法时, 通过classisa找到meta-class, 最后找到类方法的实现进行调用
  • meta-classisa指向基类的meta-class

四、class对象的superclass指针

  • 类对象的superclass指针, 指向父类的类对象

那么class中的superclass指针的作用是什么呢?

  • 现有如下代码:
Student *student = [[Student alloc] init];
[student studentInstanceMethod];
[student personInstanceMethod];
[student init];
复制代码
  • student调用对象方法studentInstanceMethod时, 会有以下查找方式
    • 通过instanceisa找到Studentclass类对象, 查看是否有studentInstanceMethod
    • 发现Studentclass对象中有studentInstanceMethod方法, 停止继续查找, 通过消息机制调用方法
  • student调用父类Person的对象方法personInstanceMethod, 会有以下查找方式
    • 通过studentisa找到Studentclass类方法, 查看是否有personInstanceMethod
    • 发现Studentclass对象中没有personInstanceMethod, 于是通过superclass指针找到Personclass对象, 查看是否有personInstanceMethod
    • Personclass对象中发现personInstanceMethod方法, 停止继续查找, 通过消息机制调用方法
  • student调用基类NSObjectinit方法时, 会有以下查找方式
    • 首先, 通过isa指针找到Studentclass对象, 查看是否有init方法
    • Studentclass对象中没有发现init方法, 于是通过superclass指针找到Personclass对象
    • Personclass中查找init方法, 结果发现Personclass对象中也没有init方法
    • 此时, 就会通过Personclass对象中的superclass指针, 找到NSObjectclass对象中, 查找init方法
    • NSObjectclass对象中, 找到了init方法, 停止继续查找, 通过消息机制调用方法

注意: classsuperclass会指向父类的class对象, 最后指向的是NSObjectclass对象, 而NSObjectclass对象中的superclass指针, 会指向nil

如果在发现NSObjectclass中也没有找到要调用的方法时, 就会报错unrecognized selector sent to instance

五、meta-class中的superclass指针

  • 与类对象的superclass指针类似, meta-class中的superclass指针指向父类的meta-class

  • 在类方法的调用上, 与实例方法调用类似

  • Studentclass要调用Person的类方法时,会先通过isa找到Studentmeta-class,然后通过superclass找到Personmeta-class,最后找到类方法的实现进行调用

注意: 基类NSObjectmeta-class对象的superclass最终指向的是NSObjectclass对象, 而不是指向nil

六、isa、superclass总结

  • isa、superclass的作用如下图:

  • intanceisa指向class

  • classisa指向meta-class

  • meta-classisa指向基类的meta-class

  • classsuperclass指向父类的class

    • 如果没有父类, superclass指针为nil
  • meta-classsuperclass指向父类的meta-class

    • 基类的meta-classsuperclass指向基类的class
  • instance调用对象方法的轨迹

    • isa找到class, 方法不存在, 就通过superclass找父类
  • class调用类方法的轨迹

    • isameta-class, 方法不存在, 就通过superclass找父类

七、验证NSObject的Meta-class对象中的superclass指向自身的Class对象

  • 上面提到过: 基类的meta-classsuperclass指向基类的class
  • 下面通过代码来进行验证
@interface Person: NSObject
+ (void)test;
@end
@implementation Person
+ (void)test {
    NSLog(@"+ [Person text] - %p", self);
}
@end
复制代码
  • Person类继承自NSObject, 有一个类对方+ (void)test, 并给出了方法的实现
NSLog(@"[Person class] - %p", [Person class]);
[Person test];

// 控制台打印: 
[Person class] - 0x100001170
+ [Person text] - 0x100001170
复制代码
  • 根据打印可以知道, 调用方法的正是Person类的Class对象

  • 现在删除test方法的实现部分, 只保留声明部分

@interface Person: NSObject
+ (void)test;
@end
@implementation Person
@end
复制代码
  • 再次调用该方法, 会报出运行时错误, test方法不存在
[Person test];
// 报错: '+[Person test]: unrecognized selector sent to class 0x100001130'
复制代码
  • 我们在给NSObject添加一个分类, 实现+ (void)test方法
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
+ (void)test
{
    NSLog(@"+ [NSObject test] - %p", self);
}
@end
复制代码
  • 再次使用Person的类对象调用test方法
NSLog(@"[Person class] - %p", [Person class]);
[Person test];
// 控制台打印: 
[Person class] - 0x100001220
+ [NSObject test] - 0x100001220
复制代码
  • 此时的调用顺序是:

    • 1、Person的类对象, 通过isa找到了Person的元类对象, 并查找有没有test方法
    • 2、由于Person的元类对象中没有test方法, 于是通过superclass找到了父类NSObject的元类对象
    • 3、在NSObject的元类对象中, 发现了test方法, 发送消息, 调用方法
  • 接着我们移除掉NSObject分类中的+ (void)test方法的实现

@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
@end
复制代码
  • 此时再次使用Person调用+ (void)test方法, 就会报运行时错误
[Person test];
reason: '+[Person test]: unrecognized selector sent to class 0x100001178'
复制代码
  • 接着在NSObject的分类中, 给出一个对象方法- (void)test的方法实现
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
- (void)test {
    NSLog(@"- [NSObject test] - %p", self);
}
@end
复制代码
  • 再次使用Person调用类方法+(void)test
NSLog(@"[Person class] - %p", [Person class]);
[Person test];
// 控制台打印:
[Person class] - 0x1000011b8
- [NSObject test] - 0x1000011b8
复制代码
  • 此时调用成功, 说明当NSObject的元类对象中没有test方法时, 就会通过superclass指针找到NSObject的类对象, 并查找有没有test方法
  • 由于在NSObject中找到了test方法, 所以会直接调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值