获取类
@interface TestClass : NSObject
@end
#import <objc/runtime.h>
TestClass *test = [TestClass new];
// 通过实例方法获取
Class c1 = test.class;
// 通过类方法获取
Class c2 = [TestClass class];
// 通过runtime获取
Class c3 = object_getClass([TestClass new]);
NSLog(@"\n%p\n%p\n%p\n", c1, c2, c3);
打印得到的类都是同一地址,可见类在内存中只有一份。
我们到源码中看一下上面几个获取类的方法
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
可见,类方法是直接返回自己,这说明类也是一个对象。实例方法实际是调用runtime方法,runtime方法是通过实例的 isa
获取到的类。
类对象的 isa 指向元类 MetaClass
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Class superclass; // 父类
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
源码可见 类 objc_class
继承自 objc_object
, 那么类对象的 isa
指向谁呢?
类对象的 isa
指向元类( MetaClass
), 我们可以结合 OC底层探索 - isa 章节里的方式,获取 isa
里存放的地址,然后与runtime方法获取到的元类地址比较。
#import <objc/runtime.h>
Class testClass = TestClass.class;
Class metaClass = objc_getMetaClass("TestClass");
通过结果可以证明,类对象的 isa
指针中存放的是元类的地址。
继承关系 & isa指向
苹果提供的资料中有这样一张图,说明了 superclass 和 isa 的指向。
从中我们可以总结:
实例对象的isa指向类对象,类对象的isa指向元类对象,元类对象的isa指向根元类对象,根元类对象的isa指向自身
父类的元类就是元类的父类,根元类的父类就是NSObject类对象,NSObject类对象的父类为nil
下面我们通过 runtime
和 lldb
分别验证一下。
先构建有继承关系的几个测试类
@interface TestClass : NSObject
@end
@interface FirstSubTestClass : TestClass
@end
@interface SecondSubTestClass : FirstSubTestClass
@end
runtime验证继承关系和部分isa关系
SecondSubTestClass *sub2 = [SecondSubTestClass new];
Class sub2Class = sub2.class;
Class sub2Superclas = class_getSuperclass(sub2Class);
Class sub2MetaClass = objc_getMetaClass("SecondSubTestClass");
Class sub2MetaSuperClass = class_getSuperclass(sub2MetaClass);
NSLog(@"SecondSubTestClass");
NSLog(@"类: %p", sub2Class);
NSLog(@"父类: %p", sub2Superclas);
NSLog(@"元类: %p", sub2MetaClass);
NSLog(@"元类的父类: %p \n", sub2MetaSuperClass);
FirstSubTestClass *sub1 = [FirstSubTestClass new];
Class sub1Class = sub1.class;
Class sub1Superclas = class_getSuperclass(sub1Class);
Class sub1MetaClass = objc_getMetaClass("FirstSubTestClass");
Class sub1MetaSuperClass = class_getSuperclass(sub1MetaClass);
NSLog(@"FirstSubTestClass");
NSLog(@"类: %p", sub1Class);
NSLog(@"父类: %p", sub1Superclas);
NSLog(@"元类: %p", sub1MetaClass);
NSLog(@"元类的父类: %p \n", sub1MetaSuperClass);
TestClass *test = [TestClass new];
Class testClass = test.class;
Class testSuperclas = class_getSuperclass(testClass);
Class testMetaClass = objc_getMetaClass("TestClass");
Class testMetaSuperClass = class_getSuperclass(testMetaClass);
NSLog(@"TestClass");
NSLog(@"类: %p", testClass);
NSLog(@"父类: %p", testSuperclas);
NSLog(@"元类: %p", testMetaClass);
NSLog(@"元类的父类: %p \n", testMetaSuperClass);
NSObject *obj = [NSObject new];
Class objClass = obj.class;
Class objSuperclas = class_getSuperclass(objClass);
Class objMetaClass = objc_getMetaClass("NSObject");
Class objMetaSuperClass = class_getSuperclass(objMetaClass);
Class objSuperMetaSuperClass = class_getSuperclass(objMetaSuperClass);
NSLog(@"NSObject");
NSLog(@"类: %p", objClass);
NSLog(@"父类: %p", objSuperclas);
NSLog(@"元类: %p", objMetaClass);
NSLog(@"元类的父类: %p \n", objMetaSuperClass);
NSLog(@"NSObject: %p \n", objSuperMetaSuperClass);
lldb验证
SecondSubTestClass *sub2 = [SecondSubTestClass new];
FirstSubTestClass *sub1 = [FirstSubTestClass new];
TestClass *test = [TestClass new];
NSObject *obj = [NSObject new];
(lldb) x/1gx sub2
0x283d34ad0: 0x000021a10064d549
(lldb) p/x 0x000021a10064d549 & 0x0000000ffffffff8ULL
(unsigned long long) $1 = 0x000000010064d548 // SecondSubTestClass 类地址
(lldb) x/2gx 0x000000010064d548
// 根据objc_class结构体可知 第1个8字节是 isa, 第2个8字节是 superclass
0x10064d548: 0x000000010064d520 0x000000010064d4f8
(lldb) p/x 0x000000010064d4f8 & 0x0000000ffffffff8ULL
(unsigned long long) $8 = 0x000000010064d4f8 // SecondSubTestClass 父类地址
(lldb) p/x 0x000000010064d520 & 0x0000000ffffffff8ULL
(unsigned long long) $12 = 0x000000010064d520 // SecondSubTestClass 元类地址
(lldb) x/2gx 0x000000010064d520
0x10064d520: 0x00000001f66a31e0 0x000000010064d4d0
(lldb) p/x 0x000000010064d4d0 & 0x0000000ffffffff8ULL
(unsigned long long) $13 = 0x000000010064d4d0 // SecondSubTestClass 元类的父类地址
(lldb) x/1gx sub1
0x283d34ae0: 0x000021a10064d4f9
(lldb) p/x 0x000021a10064d4f9 & 0x0000000ffffffff8ULL
(unsigned long long) $3 = 0x000000010064d4f8 // FirstSubTestClass 类地址
(lldb) x/2gx 0x000000010064d4f8
0x10064d4f8: 0x000000010064d4d0 0x000000010064d4a8
(lldb) p/x 0x000000010064d4a8 & 0x0000000ffffffff8ULL
(unsigned long long) $9 = 0x000000010064d4a8 // FirstSubTestClass 父类地址
(lldb) p/x 0x000000010064d4d0 & 0x0000000ffffffff8ULL
(unsigned long long) $14 = 0x000000010064d4d0 // FirstSubTestClass 元类地址
(lldb) x/2gx 0x000000010064d4d0
0x10064d4d0: 0x00000001f66a31e0 0x000000010064d480
(lldb) p/x 0x000000010064d480 & 0x0000000ffffffff8ULL
(unsigned long long) $17 = 0x000000010064d480 // FirstSubTestClass 元类的父类地址
(lldb) p/x 0x00000001f66a31e0 & 0x0000000ffffffff8ULL
(unsigned long long) $20 = 0x00000001f66a31e0 // FirstSubTestClass 元类的isa指向
(lldb) x/1gx test
0x283d34af0: 0x000021a10064d4a9
(lldb) p/x 0x000021a10064d4a9 & 0x0000000ffffffff8ULL
(unsigned long long) $5 = 0x000000010064d4a8 // TestClass 类地址
(lldb) x/2gx 0x000000010064d4a8
0x10064d4a8: 0x000000010064d480 0x00000001f66a3208
(lldb) p/x 0x00000001f66a3208 & 0x0000000ffffffff8ULL
(unsigned long long) $10 = 0x00000001f66a3208 // TestClass 父类地址
(lldb) p/x 0x000000010064d480 & 0x0000000ffffffff8ULL
(unsigned long long) $15 = 0x000000010064d480 // TestClass 元类地址
(lldb) x/2gx 0x000000010064d480
0x10064d480: 0x00000001f66a31e0 0x00000001f66a31e0
(lldb) p/x 0x00000001f66a31e0 & 0x0000000ffffffff8ULL
(unsigned long long) $18 = 0x00000001f66a31e0 // TestClass 元类的父类地址
(lldb) p/x 0x00000001f66a31e0 & 0x0000000ffffffff8ULL
(unsigned long long) $20 = 0x00000001f66a31e0 // TestClass 元类的isa指向
(lldb) x/1gx obj
0x283d34b00: 0x000021a1f66a3209
(lldb) p/x 0x000021a1f66a3209 & 0x0000000ffffffff8ULL
(unsigned long long) $7 = 0x00000001f66a3208 // NSObject 类地址
(lldb) x/2gx 0x00000001f66a3208
0x1f66a3208: 0x00000001f66a31e0 0x0000000000000000
(lldb) p/x 0x0000000000000000 & 0x0000000ffffffff8ULL
(unsigned long long) $11 = 0x0000000000000000 // NSObject 父类地址
(lldb) p/x 0x00000001f66a31e0 & 0x0000000ffffffff8ULL
(unsigned long long) $16 = 0x00000001f66a31e0 // NSObject 元类地址
(lldb) x/2gx 0x00000001f66a31e0
0x1f66a31e0: 0x00000001f66a31e0 0x00000001f66a3208
(lldb) p/x 0x00000001f66a3208 & 0x0000000ffffffff8ULL
(unsigned long long) $19 = 0x00000001f66a3208 // NSObject 元类的父类地址
(lldb) p/x 0x00000001f66a31e0 & 0x0000000ffffffff8ULL
(unsigned long long) $20 = 0x00000001f66a31e0 // NSObject 元类的isa指向