Objective-C 的类与对象
发表于:,
更新于:,
By Rm1210
缘起
那天在 sunnyxx 的博客 看到这道题目:
下面的代码报错?警告?还是正常输出什么?
|
|
大家可以到原博文看博主给出的答案。当我感觉弄懂这道题的时候,不禁感叹背后蕴含知识点的丰富,特此记录一下。
实例对象的数据结构
答案中说:
不论是实例对象还是 Class,都是 id 类型的对象
从 objc/objc.h 中可以找到 id 类型的结构体:
|
|
可以看到,代表实例对象的结构体只有一个字段:指向代表其类的 isa 指针。在上面的例子中,当我们创建 father 这个实例对象的时候,会在内存分配一个 objc_object 结构体的空间(后面还有类的实例变量的空间),这个 objc_object 的 isa 指向代表 Father 类的结构体。那代表 Father 类的结构体又是怎么样的呢?
类的数据结构
在 objc/objc.h 中可以看到代表类(Class)的数据结构的声明:
|
|
这是一个指向 objc_class 结构体的指针;在 runtime.h 中可以找到其定义:
|
|
- 为什么 objc_class 也像 objc_object 一样有一个指向代表类的结构的指针 isa?因为
Class 也是对象,既然是对象它也需要一个指针指向自己的类,这个类便是所谓的元类(Meta Class)。 - 当我们调用类的实例方法时(例如题目中的:
BOOL b1 = [father responseToSelector:@selector(responseToSelector:)];),是向实例对象 father 发送消息,然后会根据 father 的 isa 找到代表其类的 Father 的 objc_class,然后在其 methodLists 中寻找对应的 selector。 - 当我们调用类的类方法时(例如题目中的:
BOOL b2 = [Father responseToSelector:@selector(responseToSelector:)];),是直接向类对象 Father 发送消息,然后根据 Father 的 isa 找到其元类,然后在元类的 methodLists 中寻找该类方法的 selector。 - 既然元类也是 Class 类型的,它的 isa 又指向哪里呢?难道还有“元元类”。。。?这样无限延伸下去就没完了。所有有两条规则来终结这种无限延伸:
- 除了基类的元类,所有元类的 isa 指向基类的元类
- 基类的元类的 isa 指向基类的元类自己
这种关系如图所示:

图中我们还应该注意的是:
- 基类的父类指向 nil,即其 objc_class 结构体中的 super_class 字段为 NULL。如果想用 runtime 方法
objc_allocateClassPair动态生成一个基类,superclass 需要注意。
还有一个有趣的关系:
- 基类的 meta-class 的父类指向的是基类。(不明白为什么设计,请大家指教)
回归题目
正如 上一篇文章 描述的寻找 selector 的过程:
- 实例对象 father 的类对象 Father 不响应 responseToSelector:,一直沿继承体系找到 NSObject,NSObject 响应该消息,所以 b1 为 YES。
- Father 的 meta-class 不响应 responseToSelector:,一直沿继承体系找到 NSObject 的 meta-class,也不响应该消息,然后找到了 NSObject 的 meta-class 的 superclass 为 NSObject,响应该消息,所以 b2 也为 YES。
- 正如博主补充中说的:也可能是 NSObject 的 meta-class 实现了什么私有的类方法来响应对应的实例方法,那 NSObject 的 meta-class 就会直接响应该消息返回 YES。
致谢。这道题目让我一下子记住了上面那个关系图,感谢题目的构思者 :)。(应该就是博主sunnyxx吧?)