OC对象分类

OC对象

实例对象

每次通过 alloc 产生出的对象,每个产生的对象都占用着不同内存

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface MyClass:NSObejct
{
@public
成员1;
成员2;
}
@end

int main()
{
MyClass *instanceClass = [[MyClass alloc] init];

return 0;
}

perClass 为实例对象,实例对象中只存储 isa 指针和各个类成员的值,不存储类的方法。

类对象

一个类无论有多少个实例对象,有且仅有一份类对象

使用三种方法获取类对象

  • [类型名 class]
  • [实例对象 class]
  • object_getClass(实例对象)

类对象在内存中所包含的信息有

  • isa指针
  • superclass指针
  • 类的属性信息 (property)
  • 类的对象方法信息 (instance method)
  • 类的协议信息 (protocol)
  • 类的成员变量信息 (ivar)

实例对象的方法调用转为C语言,例如

1
[object MyFunc] == objec_msgSend(object, @selector(MyFunc));

元类对象

Class objectMetaClass = object_getClass([MyClass class])

因为都是 class 类型,所以元类对象和类对象的内存结构是相同的

元类对象中包含

  • isa指针
  • superclass指针
  • 类方法

对象的 isa 指针指向的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//实例对象
MyClass * instanceClass1 = [[MyClass alloc] init];
MyClass * instanceClass2 = [[MyClass alloc] init];

//类对象
Class class1 = object_getClass(instanceClass1);
Class class2 = object_getClass(instanceClass1);

//元类对象
Class metaClass1 = object_getClass(class1);
Class metaClass2 = object_getClass(class2);

//打印地址
printf("instanceClass1:%p\r\ninstanceClass2:%p\r\nclass1:%p\r\nclass2:%p\r\nmetaClass1:%p\r\nmetaClass2:%p\r\n", instanceClass1,instanceClass2,class1,class2,metaClass1,metaClass2);

//输出
instanceClass1:0x10281c900
instanceClass2:0x10281c710
class1: 0x1000013f8
class2: 0x1000013f8
metaClass1: 0x1000013d0
metaClass2: 0x1000013d0

输出结果所示,虽然实例对象的内存不一致,但它们类对象和元类对象所指向的都是同一块区域

于是打开 OC 的源码,找到 objc-class.mm 中的 object_getClass()方法

1
2
3
4
5
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}

可以看到 object_getClass 函数的返回值是 isa 指针

实例对象的isa

通过 lldb 来查看实例对象 isa 的指向

1
2
(lldb) p/x instanceClass1->isa
(Class) $3 = 0x001d8001000013f9 MyClass

类对象的内存值为和实例对象所指向的值不相同,于是打开源码 getIsa() 发现在返回类对象的地址时需要做一次与运算才能得到正确的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#   define ISA_MASK        0x00007ffffffffff8ULL
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}

与 运算的值是一个宏 ISA_MASK ,值为 0x00007ffffffffff8ULL

1
2
lldb) p/x 0x001d8001000013f9 & 0x00007ffffffffff8
(long) $4 = 0x00000001000013f8

类对象的isa

由于限制,无法使用 lldb 打印类对象 isa 所指向的地址,因此需要自定一个结构体,结构体中仅有一个 isa 成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct classStruct
{
Class isa;
};

int main()
{
//实例对象
MyClass * instanceClass1 = [[MyClass alloc] init];
MyClass * instanceClass2 = [[MyClass alloc] init];

//类对象
struct classStruct* class1 = (__bridge struct classStruct *)(object_getClass(instanceClass1));
Class class2 = (object_getClass(instanceClass1));

//元类对象
Class metaClass1 = object_getClass(class2);
}

此时可以直接访问类对象 class1 中的 isa 成员,将 isa 所指向的地址和 0x00007ffffffffff8 做与元算,结果正好为元类对象 metaClass 的地址

1
2
3
4
5
6
(lldb) p/x class1->isa
(Class) $0 = 0x001d8001000013d1
(lldb) p/x metaClass1
(Class) $1 = 0x00000001000013d0
(lldb) p/x 0x00007ffffffffff8 & 0x001d8001000013d1
(long) $2 = 0x00000001000013d0

所以最后用一个图来描述它们之间的关系

元类对象的isa

根据苹果官方的图示,所有的元类对象的 isa 都指向基类的元类对象(Root 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
26
27
28
29
@interface MyClass : NSObject
{

}
@end

@implementation MyClass

@end

struct classStruct
{
Class isa;
Class superClass;
};

int main()
{
//实例对象
MyClass * instanceClass = [[MyClass alloc] init];

//类对象
Class class = (object_getClass(instanceClass));

//元类对象
Class metaClass1 = object_getClass(class);
//将元类对象转为自定义的结构体指针
struct classStruct* metaClass2 = (__bridge struct classStruct *)metaClass1;
}

MyClass 继承自 NSObject ,所以 MyClass 的元类对象的 isasuperclass 都应该指向 NSObject 的元类对象

接下来使用 lldb ,仅需验证 metaClass2isasuperclass 均指向同一个地址即可

1
2
3
4
5
6
(lldb) p/x metaClass2->superClass
(Class) $0 = 0x00007fff8aed60f0
(lldb) p/x metaClass2->isa
(Class) $1 = 0x001dffff8aed60f1
(lldb) p/x 0x00007ffffffffff8 & 0x001dffff8aed60f1
(long) $2 = 0x00007fff8aed60f0

此时 $0$1 & isa_mask 的值均指向 0x00007fff8aed60f0 也就是 NSObject 的元类对象