前言

OC是一门面向对象的语言,这篇文章先来看看OC的类和对象(Class && Object)

正文

  1. OC中的对象本质上是一个objc_object结构体
  2. OC中的类本质上是一个objc_class结构体
  3. id:指向objc_object的结构体指针
  4. Class:指向objc_class的结构体指针

objc_class 和 objc_object

先来看看两个结构体的结构(先只考虑结构体的成员变量,不考虑它的成员函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
struct objc_object {
private:
isa_t isa;
public:
// function here
}

struct objc_class : objc_object {
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits;
// method here
}

objc_object 定义在 objc-private.h 里面,该结构体只包含一个叫做 isa 的isa_t类型的变量

objc_class 定义在 objc-runtime-new.h 里面,它继承自objc_object,所以除了isa成员变量之外,它还有:

  1. 指向另一个objc_class结构体的指针 super_class;这一个objc_class包含了父类的信息
  2. 一个包含函数缓存的cache_t类型变量 cache
  3. 一个包含类的方法,属性等信息的class_data_bits_t类型的变量 bits

编译结束之后,OC的每个类都是以objc_class的结构体形式存在于内存之中,而且在内存中的位置已经固定。运行期间,创建新的对象的时候,也是创建objc_object的结构体

isa

成员变量isa包含了该对象,或者该类的信息。isa变量的类型是isa_t,定义在objc_private.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
union isa_t 
{
Class cls;
uintptr_t bits;

# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
};
}

ps : 以上代码是在x86_64上的实现(Mac上面);iPhone(arm64等)上面的实现大同小异。具体实现可以参见runtime源码

has_assoc

表示该对象是否有关联对象

has_cxx_dtor

表示是否有析构函数

shiftcls

表示对象所属的类或者类所属的元类(meta class)的地址,也就是指向一个objc_class的指针,不过该指针只有44位。

64位系统的指针占用64bit的内存,但是使用整个指针大小来存储地址有点浪费。在mac的64位系统上面,使用47位作为指针,其他的17位用于其他目的(iPhone上面只使用33位)。又由于所有对象按照8字节对齐,所以指针都是能被8整除的,也就是后3bit均为0;所以类指针的实际有效位数为 47 - 3 = 44 位。这也是shiftcls只有44位的原因

isa初始化

在创建新的objc_object的时候,会使用objc_object的成员函数initIsa来初始化isa变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
initIsa(cls, true, hasCxxDtor);
}

inline void
objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor)
{
if (!indexed) {
isa.cls = cls;
} else {
isa.bits = ISA_MAGIC_VALUE;
isa.has_cxx_dtor = hasCxxDtor;
isa.shiftcls = (uintptr_t)cls >> 3;
}
}

这里的indexed用来标记isa是否有结构体部分:

  1. 如果indexed为0,没有结构体部分,直接将对象所属的类的地址赋予cls变量
  2. 如果indexed为1,有结构体部分,先使用ISA_MAGIC_VALUE 给isa这个64位union初始化,然后在对64位上的每一位单独设置。比如设置是否含有析构函数(has_cxx_dtor),设置对象所属类的地址(shiftcls)。注意这里设置类地址的时候是将cls右移3位,再赋值给shiftcls的,原因就是类地址的最后3bit没有实际作用

isa()方法

该方法用来获取对象所属的类的指针,也就是表示该对象是一个什么类

1
2
3
4
5
6
inline Class 
objc_object::ISA()
{
return (Class)(isa.bits & ISA_MASK);
}
// #define ISA_MASK 0x00007ffffffffff8ULL

返回的是一个64位的指向objc_class结构体的指针,其中的4-47bit为shiftcls的值,其他bit都是0

总结

OC的对象和类在内存中都是以结构体的形式存在的,类对应objc_class结构体,对象对应objc_object结构体。

在创建这两种结构体的过程中都会初始化isa变量。isa变量存放着对象所属的类的信息,或者类所属的元类的信息。关于元类的相关内容,可自行google。

另外,在创建objc_class的结构体的时候还会初始化相应的cache和bits变量,在后续的章节中将会说道

参考