Hello, World!

Objective-C对象模型

字数统计: 1.6k阅读时长: 6 min
2019/03/27 Share

isa指针

Objective-C是一门面向对象的编程语言,每一个对象都是一个类的实例。在Objective-C语言的内部,每一个对象都有一个名为isa的指针,指向该对象所属的类。每一个类描述了一系列它的实例的特点,包括成员变量列表、成员函数列表等。每一个对象都可以接受消息,而对象能够接受的消息列表保存在它所对应的类中。

在Xcode中,打开NSObject的定义头文件,可以看到,NSObject就是一个包含isa指针的结构体,如下所示。

1
2
3
4
5
6
7
// NSObject.h
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
1
2
3
4
5
6
7
8
// objc.h
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};

按照面向对象语言的设计原则,所有事物都应该是对象(严格来说Objective-C并没有完全做到这一点,因为它有像int、double这样的简单变量类型)。在Objective-C语言中,每一个类实际上也是一个对象。每一个类也有一个名为isa的指针。每一个类也可以接收消息,例如代码[NSObject alloc],就是向NSObject这个类发送名为alloc的消息。

在Xcode中,打开Class的定义头文件,可以看到,Class也是一个包含isa指针的结构体,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// runtime.h
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

因为类也是一个对象,所有它也必须是另一个类的实例,这个类是元类(metaclass)。元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有,则该元类会向它的父类查找该方法,这样可以一直找到继承链的头。

元类也是一个对象,那么元类的isa指针又指向哪里呢?为了设计上的完整,所有元类的isa指针都会指向一个根元类(root metaclass)。根元类本身的isa指针指向自己,这样就形成了一个闭环。上面提到,一个对象能够接收的消息列表是保存咋它所对应的类中的。在实际编程中,我们几乎不会遇到向元类发信息的情况,那它的isa指针在实际上很少用到。不过这么设计保证了面向对象概念在Objective-C语言中完整,即语言中的所有事物都是对象,都有isa指针。

再来看看继承关系,由于类方法的定义是保存在元类中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以,为了保证父类的类方法在子类中可以被调用,所有子类的元类都会继承父类的元类,换言之,类对象和元类对象有着同样的继承关系。

下面这张图更清楚的说明了isa和继承的关系。
isa和继承的关系图

从图中可以看出:

  • NSObject的类中定义了实例方法,例如-(id)init和-(void)dealloc方法。
  • NSObject的元类中定义了类方法,例如+(id)alloc、+(void)load和+(void)initialize方法。
  • NSObject的元类继承自NSObject类,所以NSObject是所有类的根,因此NSObject中定义的实例方法可以被所有对象调用,例如-(id)init和-(void)dealloc方法。
  • NSObject的元类的isa指向自己。

类的成员变量

如果把类的实例看成一个结构体的话,上面说的isa指针就是这个结构体的第一个成员变量,而类的其他成员变量依次排列在结构体中。如下图所示。
类的实例结构图

因为对象在内存中的排布可以看成一个结构体,该结构体的大小并不能动态变化,所以无法在运行时动态地给对象添加成员变量。

相应地,对象的方法定义都保存在类的可变区域中,方法的定义列表是一个名为methodLists的指针的指针,通过修改该指针指向的指针的值,就可以动态地为某一个类增加成员方法。这也是Category实现的原理。同时也说明了为什么Category只可为对象增加成员方法,却不能增加成员变量。

因为isa本身也只是一个指针,所以除了对象的方法可以动态地修改外,我们也可以在运行时动态地修改isa指针的值,达到替换对象整个行为的目的,不过该应用场景较少。

objc_class详解

类对象是由Class类型来表示的,它是一个objc_class结构类型,如下所示。

  • objc_class结构类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    struct objc_class {
    // 类对象的指针指向其所属的类,即元类。元类中存储着类对象的类方法,当访问某个类的类方法时会通过该isa指针从元类中寻找方法对应的函数指针。
    Class _Nonnull isa OBJC_ISA_AVAILABILITY;

    #if !__OBJC2__
    // 指向该类所继承的父类对象,如果该类已经是最顶层的根类(如NSObject或NSProxy), 则为NULL。
    Class _Nullable super_class OBJC2_UNAVAILABLE;
    // 类的名称
    const char * _Nonnull name OBJC2_UNAVAILABLE;
    long version OBJC2_UNAVAILABLE;
    long info OBJC2_UNAVAILABLE;
    // 类的实例大小
    long instance_size OBJC2_UNAVAILABLE;
    // 类成员变量列表
    struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
    // 类成员函数列表
    struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
    // 缓存调用过的方法
    struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
    // 类成员协议列表
    struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
    #endif

    } OBJC2_UNAVAILABLE;
    /* Use `Class` instead of `struct objc_class *` */
  • objc_ivar结构类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct objc_ivar {
    // 成员变量名称
    char * _Nullable ivar_name OBJC2_UNAVAILABLE;
    // 成员变量类型
    char * _Nullable ivar_type OBJC2_UNAVAILABLE;
    // 成员变量偏移
    int ivar_offset OBJC2_UNAVAILABLE;
    #ifdef __LP64__
    int space OBJC2_UNAVAILABLE;
    #endif
    } OBJC2_UNAVAILABLE;
  • objc_method结构类型

    1
    2
    3
    4
    5
    6
    7
    8
    struct objc_method {
    // 选择器(方法名称),通常可以把它理解为一个字符串。
    SEL _Nonnull method_name OBJC2_UNAVAILABLE;
    // 方法编码
    char * _Nullable method_types OBJC2_UNAVAILABLE;
    // 方法地址 函数实现体指针
    IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
    } OBJC2_UNAVAILABLE;
CATALOG
  1. 1. isa指针
  2. 2. 类的成员变量
  3. 3. objc_class详解