Objective-C 的类与对象

发表于:, 更新于:, By Rm1210
大纲
  1. 1. 缘起
  2. 2. 实例对象的数据结构
  3. 3. 类的数据结构
  4. 4. 回归题目

缘起

那天在 sunnyxx 的博客 看到这道题目:

下面的代码报错?警告?还是正常输出什么?

1
2
3
4
Father *father = [Father new];
BOOL b1 = [father responseToSelector:@selector(responseToSelector:)];
BOOL b2 = [Father responseToSelector:@selector(responseToSelector:)];
NSLog(@"%d, %d", b1, b2);

大家可以到原博文看博主给出的答案。当我感觉弄懂这道题的时候,不禁感叹背后蕴含知识点的丰富,特此记录一下。

实例对象的数据结构

答案中说:

不论是实例对象还是 Class,都是 id 类型的对象

objc/objc.h 中可以找到 id 类型的结构体:

1
2
3
4
5
6
7
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;

可以看到,代表实例对象的结构体只有一个字段:指向代表其类的 isa 指针。在上面的例子中,当我们创建 father 这个实例对象的时候,会在内存分配一个 objc_object 结构体的空间(后面还有类的实例变量的空间),这个 objc_object 的 isa 指向代表 Father 类的结构体。那代表 Father 类的结构体又是怎么样的呢?

类的数据结构

objc/objc.h 中可以看到代表类(Class)的数据结构的声明:

1
2
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

这是一个指向 objc_class 结构体的指针;在 runtime.h 中可以找到其定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#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 *` */
  • 为什么 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 又指向哪里呢?难道还有“元元类”。。。?这样无限延伸下去就没完了。所有有两条规则来终结这种无限延伸:
    1. 除了基类的元类,所有元类的 isa 指向基类的元类
    2. 基类的元类的 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吧?)