美文网首页
iOS 对象中的 isa、superclass 指针总结

iOS 对象中的 isa、superclass 指针总结

作者: 笔头还没烂 | 来源:发表于2023-04-10 22:56 被阅读0次

一、以下几点,instance 指的是实例对象,class 指的是类对象,meta-class 指的是元类对象

  1. instance 的 isa 指向 class
  2. class 的 isa 指向 meta-class
  3. meta-class 的 isa 指向基类的 meta-class
  4. class 的 superclass 指向父类的 class
    • 如果没有父类,superclass 指针为 nil
  5. meta-class 的 superclass 指向父类的 meta-class
    • 基类的 meta-class 的 superclass 指向基类的 class


      isa、superclass指针.png

二、调用轨迹:

  1. instance 调用对象方法的轨迹
    • isa 找到 class,方法不存在,就通过 superclass 找父类
  2. class 调用类方法的轨迹
    • isa 找 meta-class,方法不存在,就通过 superclass 找父类

三、isa 指针问题:

  1. 示例代码:

    #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 ,即可找到元类对象
  2. 思考:类对象的 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;
    }
    
  3. 如何证明 对象的对象方法信息、属性信息、协议信息、成员变量信息是存储在类对象中?如何证明对象的类方法是存储在元类对象中?

    (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

    从上面的代码运行结果可以看出,实例方法确实存储在类对象中,类方法确实存储在元类对象中。

相关文章

  • iOS-浅谈OC中isa和superclass的指针指向

    目录 isa指针----isa指针指向superclass指针----class对象的superclass指针--...

  • iOS:isa与superclass

    目录一,对象的三种类型二,对象的存储信息三,isa指针四,superclass指针五,isa和superclass...

  • 2-OC对象的分类

    OC对象分为三类 instance对象isa指针其他成员变量 class对象isa指针superclass指针协议...

  • iOS - Runtime基础

    Runtime合集iOS - isa、superclass指针,元类superclass指向基类本身[https:...

  • 02-OC中对象的isa指针和superclass指针

    OC中的isa指针和superclass指针 OC中的对象分为哪一类 instance对象(实例对象) class...

  • iOS - isa、superclass指针,元类supercl

    本文已同步至掘金:iOS - isa、superclass指针,元类superclass指向基类本身[https:...

  • 深入理解Runtime

    目录 1.OC对象1.OC对象的分类2.isa指针、superClass指针总结 2.对象底层数据结构1.实例对象...

  • isa和superclass

    D、题:对象的isa指针指向哪里? 拓展: class对象的superclass指针指向哪里? meta-clas...

  • NSObject对象(1)

    对象分3种 实例对象,类对象,元类 实例对象中有isa指针但是没有superClass指针 对象 instance...

  • iOS class(类)对象

    每个类在内存中有且只有一个class对象class对象在内存中存储的信息包括isa指针superclass指针类的...

网友评论

      本文标题:iOS 对象中的 isa、superclass 指针总结

      本文链接:https://www.haomeiwen.com/subject/aygwddtx.html