一、以下几点,instance 指的是实例对象,class 指的是类对象,meta-class 指的是元类对象
- instance 的 isa 指向 class
- class 的 isa 指向 meta-class
- meta-class 的 isa 指向基类的 meta-class
- class 的 superclass 指向父类的 class
- 如果没有父类,superclass 指针为 nil
- meta-class 的 superclass 指向父类的 meta-class
-
基类的 meta-class 的 superclass 指向基类的 class
isa、superclass指针.png
-
二、调用轨迹:
- instance 调用对象方法的轨迹
- isa 找到 class,方法不存在,就通过 superclass 找父类
- class 调用类方法的轨迹
- isa 找 meta-class,方法不存在,就通过 superclass 找父类
三、isa 指针问题:
-
示例代码:
#import <Foundation/Foundation.h> #import <objc/runtime.h> @interface Person : NSObject @end @implementation Person @end @interface Student : Person @end @implementation Student @end struct gq_objc_class { Class isa; }; int main(int argc, const char * argv[]) { @autoreleasepool { // 验证:实例对象的 isa 指定指向的就是类对象 //实例对象 Person *person = [[Person alloc] init]; //类对象 Class personClass = [Person class]; /* 通过断点及lldb动态调试器的调试,可获取到实例对象的 isa 指针地址值是: (lldb) p/x (long)person->isa (long) $0 = 0x01000001000080c1 类对象的地址值是: (lldb) p/x (long)personClass (long) $1 = 0x00000001000080c0 isa 指针地址值 & ISA_MASK 的地址值,结果是: (lldb) p/x (long)(0x01000001000080c1 & 0x00007ffffffffff8ULL) (long) $2 = 0x00000001000080c0 */ //验证:类对象的 isa 指针指向元类对象 //因为上面的Class无法直接拿到isa成员变量,所以转成下面的自定义的结构体类型 struct gq_objc_class *personClass2 = (__bridge struct gq_objc_class *)([Person class]); /** (lldb) p/x (long)personClass2->isa (long) $0 = 0x0000000100008098 */ //元类对象 Class personMetaClass = object_getClass(personClass); /** (lldb) p/x (long)personMetaClass (long) $1 = 0x0000000100008098 */ /** (lldb) p/x (long)(0x0000000100008098 & 0x00007ffffffffff8ULL) (long) $2 = 0x0000000100008098 */ NSLog(@"%p,%p,%p",person,personClass,personMetaClass); //0x600000008030,0x1000080c0,0x100008098 /** 结论:只要拿到实例对象的 isa ,即可找到类对象;只要拿到类对象的 isa ,即可找到元类对象 */ } return 0; }
小结:
- 实例对象的 isa 指针的地址值并不是直接指向类对象,需要 &(逻辑与)上 ISA_MASK 才等于类对象的地址值;
- 只要拿到实例对象的 isa ,即可找到类对象;只要拿到类对象的 isa ,即可找到元类对象
-
思考:类对象的 superClass 指针是否也存在这种情况?是否也需要&上ISA_MASK才等于元类对象的地址值?
#import <Foundation/Foundation.h> #import <objc/runtime.h> @interface Person : NSObject @end @implementation Person @end @interface Student : Person @end @implementation Student @end /** typedef struct objc_class *Class; /// Represents an instance of a class. struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY; }; struct objc_class : objc_object { // Class ISA; Class superclass; ... } 类比上面苹果提供的官方源码,我们可以自定义自己的结构体类型 */ struct gq_objc_class { Class isa; Class superClass; }; int main(int argc, const char * argv[]) { @autoreleasepool { //实例对象 Student *stu = [[Student alloc] init]; //类对象(存储着 isa、superClass指针等信息) Class studentClass = [Student class]; //转成自定义类型的结构体(转成自定义类型的结构体的目的是为了获取 superClass 指针的地址值) struct gq_objc_class *studentClass2 = (__bridge struct gq_objc_class *)([Student class]); /** (lldb) p/x studentClass2->superClass (Class) $0 = 0x0000000100008158 Person */ Person *p = [Person class]; /** (Person *) $1 = 0x0000000100008158 */ //证实:通过打印类对象的 superClass 指针地址与它父类的类对象的地址,并通过比较,发现它俩是相等的。由此可见,类对象的 superClass指针确实直接指向父类的类对象 } return 0; }
-
如何证明 对象的对象方法信息、属性信息、协议信息、成员变量信息是存储在类对象中?如何证明对象的类方法是存储在元类对象中?
(1)查看苹果 objc4 源码,相关源码如下:
struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; // 方法缓存 class_data_bits_t bits; // 用于获取具体的类信息 class_rw_t *data() const { return bits.data(); } ... } // class_rw_t 中 rw 一般是 readwrite(可读可写) 的缩写,t 一般是 table (表格)的缩写 // 跳转到 class_rw_t 这个类型的定义中去,可得下面的源码: struct class_rw_t { ... private: using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>; ... public: ... const method_array_t methods() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods; } else { return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods}; } } const property_array_t properties() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties; } else { return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties}; } } const protocol_array_t protocols() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols; } else { return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols}; } } }; //与class_rw_t相类似的是class_ro_t,ro 是 readonly 的缩写,ro_t 即只读表 //只读表的源码如下: struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize;//例如:class_getInstanceSize 方法,获取实例大小 ... } //可读可写表的源码如下: struct class_rw_ext_t { DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t) class_ro_t_authed_ptr<const class_ro_t> ro; method_array_t methods; property_array_t properties; protocol_array_t protocols; const char *demangledName; uint32_t version; };
(2)还可以利用 runtime 库中的 API 来验证,代码如下:
#import "ViewController.h" #import <objc/runtime.h> @interface MyClass : NSObject - (void)instanceMethod; + (void)classMethod; @end @implementation MyClass - (void)instanceMethod { NSLog(@"This is an instance method"); } + (void)classMethod { NSLog(@"This is a class method"); } @end @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; MyClass *instance = [[MyClass alloc] init]; //获取 MyClass 的类对象 Class myClass = [MyClass class]; //获取 MyClass 的元类对象 Class metaClass = object_getClass(myClass); //从 myClass 类对象中获取方法列表 unsigned int instanceMethodCount; Method *instanceMethods = class_copyMethodList(myClass, &instanceMethodCount); //从 metaClass 元类对象中获取方法列表 unsigned int classMethodCount; Method *classMethods = class_copyMethodList(metaClass, &classMethodCount); //遍历从 myClass 类对象中获取到的方法列表,查看方法名 for (unsigned int i = 0; i < instanceMethodCount; i++) { Method method = instanceMethods[i]; SEL sel = method_getName(method); NSString *methodName = NSStringFromSelector(sel); NSLog(@"类对象存储的方法:%@",methodName); } //遍历从 metaClass 元类对象中获取到的方法列表,查看方法 for (unsigned int i = 0; i < classMethodCount; i++) { Method method = classMethods[i]; SEL sel = method_getName(method); NSString *methodName = NSStringFromSelector(sel); NSLog(@"元类对象存储的方法:%@",methodName); } //释放实例方法列表和类方法列表 free(instanceMethods); free(classMethods); } @end
输出结果如下:
类对象存储的方法:instanceMethod
元类对象存储的方法:classMethod从上面的代码运行结果可以看出,实例方法确实存储在类对象中,类方法确实存储在元类对象中。
网友评论