美文网首页
iOS - isa、superclass指针,元类supercl

iOS - isa、superclass指针,元类supercl

作者: 码代码的小马 | 来源:发表于2021-05-04 09:48 被阅读0次

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

    一. 对象

    instance对象,也称实例对象
    class对象,也称类对象
    meta-calss对象,也称元类对象

    本文将探讨如下问题:

    • 这些对象中包含了什么信息
    • 这些信息如何关联
    • 成员变量是存在哪里的
    • 类方法与实例方法是存在哪里的
    • 如何找到superclass

    instance对象

    1. 什么是instance对象

    通过+alloc之后的都是instance对象.这里还需要强调的是:instance对象不仅仅是NSObject对象,还有一个代理类NSProxy,也能创建instance对象

    // 创建一个对象
    NSObject* obj = [NSObject alloc];
    
    

    Class对象

    1. 获取class对象

    创建一个继承自NSObjectClassObject

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        ClassObject *clsObj = [[ClassObject alloc] init];
        [self fetchClassWithClassObject:clsObj];
    }
    
    //获取class的所有方法
    - (void)fetchClassWithClassObject:(ClassObject *)clsObj {
        Class objClass1 = [clsObj class];
        Class objClass2 = [ClassObject class];
        Class objClass3 = object_getClass(clsObj);
        
        NSLog(@"\n objClass1 = %@ \n objClass2 = %@ \n objClass3 = %@ \n",NSStringFromClass(objClass1), NSStringFromClass(objClass2), NSStringFromClass(objClass3) );
        NSLog(@"\n\n objClass1 = %p \n objClass2 = %p \n objClass3 = %p", objClass1, objClass2, objClass3);
    }
    
    //打印结果
    2021-05-04 10:34:36.333929+0800 ClassDemo[26079:8911440] 
     objClass1 = ClassObject 
     objClass2 = ClassObject 
     objClass3 = ClassObject
    2021-05-04 10:34:36.334052+0800 ClassDemo[26079:8911440] 
    
     objClass1 = 0x109a61548 
     objClass2 = 0x109a61548 
     objClass3 = 0x109a61548
     
    

    打印可见,不管用什么方式获取的class对象都是一样的,打印地址也一致,说明在项目中一个Class仅有一个对象,但是上面三种获取class的方式有什么不同呢?
    前两种是通过方法获取的,直接获取的是当前instance的Class, 但是第三种方式不一样,这种方式获取的是当前instance的isa的值

    可以这样做个实验:给clsObj添加一个kvo

    [clsObj addObserver:self forKeyPath:@"class" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    

    打印结果变为

    2021-05-04 10:54:32.415419+0800 ClassDemo[28990:8934226] 
     objClass1 = ClassObject 
     objClass2 = ClassObject 
     objClass3 = NSKVONotifying_ClassObject
    2021-05-04 10:54:32.415562+0800 ClassDemo[28990:8934226] 
    
     objClass1 = 0x10fea3550 
     objClass2 = 0x10fea3550 
     objClass3 = 0x600001300240
    

    第三个值变成了NSKVONotifying_ClassObject

    2. class对象中的信息

    1. isa
    2. superclass
    3. 属性 property
    4. instance 方法
    5. 协议 protocal
    6. 成员变量,这里的成员变量信息并不是一个 instance 中成员变量的值,而是指在这个 Class 中有哪些成员变量,是 NSSting 的,还是 int 类型的
    7. 其它

    meta-class 对象

    1. 获取meta-class对象

    在开发中是不会手动去+alloc一个元类对象,可以通过object_getClass函数获取classisa类取之,代码如下

    - (void)fetchMetaClassWithClassObject:(ClassObject *)clsObj {
        //1. 获取一个对象的isa
        Class objIsa = object_getClass(clsObj);
        //获取元类对象
        Class metaClass = object_getClass(objIsa);
        
        NSLog(@" \n metaClass = %p \n metaClass = %@", metaClass, NSStringFromClass(metaClass));
    }
    

    会发现,元类还是当前的Class,但是是另一个对象地址
    其次,不管是class对象还是元类对象,其类型都是Class,说明在内存结构上是一致的,但是其包含的信息含义是不一样的,其用途也不一样

    meta-class 对象中的信息

    1. isa
    2. superclass
    3. 类方法信息
    4. 其它

    对象总结

    • 总共有三种对象:instance对象、class对象,meta-class对象
    • 成员变量的值都存在instance中
    • 属性、instance(实例)方法、协议protocol都存于class中
    • 类方法都存于meta-class中
    image

    二. isa

    以上的三种对象是如何关联起来的呢?是通过isa关联的:

    instance对象的isa的值是class对象,class对象的isa的值是meta-calss对象

    既然实例方法是存在于class对象中,那么当给一个instance对象发送消息的时候,是如何找到具体的实现方法的呢?

    当调用实例方法的时候,通过instance对象中的isa找到class,找到对应的实例方法的实现

    同理,类方法的调用也是一样的:

    当调用类方法时,通过class对象的isa指针找到meta-class, 并找到对应的类方法实现

    三. superclass

    superclass指针是相对于class对象meta-class对象来说的
    定义两个Class:Person继承于NSObject,Student继承于Person。现在有个场景:通过Student的instance对象调用Person中实现的实例方法,具体的调用过程如下:

    通过Student类的instance对象的isa找到对应的Student类的class对象,但没有找到相关的实现,系统会继续到superclass中找,于是会到Person类的class对象中找到具体的实现并调用

    类方法的调用也是一样的

    四. isa与superclass

    在NSObject的头文件中,可以看到一个Class类型的isa成员变量

    @interface NSObject <NSObject> {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wobjc-interface-ivars"
        Class isa  OBJC_ISA_AVAILABILITY;
    #pragma clang diagnostic pop
    }
    

    在NSObject的协议里有个Class类型的成员变量superclass

    @protocol NSObject
    ···
    @property (readonly) Class superclass;
    ···
    @end
    
    

    接下来,我们看一个经典的指针逻辑分析图,分别看Objective-C面向对象语言设计、函数调用、成员变量的存储和访问是如何实现的,为什么不能多继承

    image

    这张图可以看到三个类:RootclassSuperclassSubClass,分别为基类父类子类,图中左到右分别为实例对象类对象元类, 举个例子:我们创建一个Person类继承自NSObject, 创建一个Student类继承自Person。则NSObjectPeronStudent分别对应RootclassSuperclassSubClass,左边的Instance对象代表实例对象,也就是我们alloc出来的实例

    由图可知:

    isa

    • 实例的isa指向类对象,类对象的isa指向元类对象
    • meta-class的isa都指向基类的meta-class

    superclass

    • class的superclass指向父类,如果没有父类,指向nil
    • meta的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class

    调用轨迹

    • instance对象:isa找到class,方法如果不存在,就通过superclass找父类
    • class对象:isa找到meta-class,方法如果不存在,就通过superclass找父类

    五. isa、class、superclass关系求证

    上面说到一句话

    instance对象的isa值是class对象, class的isa值是meta-class对象

    上图也有所体现了,再把上图标识序号,如下:


    image

    接下来就是证明这5条线的正确性,创建Person类继承自NSObject,代码如下:

        Person *instanceObj = [[Person alloc] init];
        // 第一条线 (instance的isa值是Class对象)
        Class classCls  = object_getClass(instanceObj);
        // 第二条线 (Class对象的isa值是meta-class)
        Class metaCls = object_getClass(classCls);
        // 第三条线 (meta-class的Superclass 是 RootClass的meta-class)
        Class rootMetaCls0 = class_getSuperclass(metaCls);
        // 第四条线 (meta-class的isa值是RootClass 的meta-class)
        Class rootMetaCls1 = object_getClass(metaCls);
        
        //RootClass的meta-class
        Class rootMetaCls = object_getClass(rootMetaCls0);
        
        NSLog(@"\n instanceObj = %p \n classCls = %p \n metaCls = %p \n rootMetaCls0 = %p \n rootMetaCls1 = %p \n rootMetaCls  = %p\n", instanceObj, classCls, metaCls, rootMetaCls0, rootMetaCls1, rootMetaCls);
    

    打印结果:

    2021-05-04 16:25:08.423125+0800 ClassDemo[32740:9240333] 
     instanceObj  = 0x600002d7c0e0 
     classCls     = 0x109a21800 
     metaCls      = 0x109a217d8 
     rootMetaCls0 = 0x7fff86d48638 
     rootMetaCls1 = 0x7fff86d48638 
     rootMetaCls  = 0x7fff86d48638
    
    

    注意下后边三个值是一样的,现在应该理清了,但是上面的代码中,没有isa相关的,我们仅仅是获取了对应对象的类型(Class)而已,现在想要看看对应的isa值是多少,由于isa有保护,我们可以通过kvc方式获取,也可以参考写个Class结构体.通过Bridge强转桥接C/C++再访问isa对象地址,前提是你要通过clang编译器编译出对象结构体,知道它长什么样。
    我们采用kvc的方式,对代码稍作修改,查看isa值:

        NSObject *object = [[NSObject alloc] init];
        Class rootCls = object_getClass(object);
        
        Person *instanceObj = [[Person alloc] init];
        // 第一条线 (instance的isa值是Class对象)
        Class classCls  = object_getClass(instanceObj);
        // 第二条线 (Class对象的isa值是meta-class)
        Class metaCls = object_getClass(classCls);
        // 第三条线 (meta-class的Superclass 是 RootClass的meta-class)
        Class rootMetaCls0 = class_getSuperclass(metaCls);
        // 第四条线 (meta-class的isa值是RootClass 的meta-class)
        Class rootMetaCls1 = object_getClass(metaCls);
        
        //RootClass的meta-class
        Class rootMetaCls = object_getClass(rootMetaCls0);
        
        //root-meta-class 的superclass
        Class rootMetaSuperCls = class_getSuperclass(rootMetaCls0);
        
        NSLog(@"\n rootCls = %p \n instanceObj = %p \n classCls = %p \n metaCls = %p \n rootMetaCls0 = %p \n rootMetaCls1 = %p \n rootMetaCls  = %p \n rootMetaSuperCls = %p \n", rootCls, instanceObj, classCls, metaCls, rootMetaCls0, rootMetaCls1, rootMetaCls, rootMetaSuperCls);
        //通过kvc方式获取isa指针
        NSLog(@"\n instanceObj-isa = %p \n classCls-isa = %p \n metaCls-isa = %p \n rootMetaCls0-isa = %p \n", [instanceObj valueForKey:@"isa"], [classCls valueForKey:@"isa"], [metaCls valueForKey:@"isa"], [rootMetaCls0 valueForKey:@"isa"]);
    
    

    打印结果为:

    2021-05-04 16:55:11.688427+0800 ClassDemo[49442:9299385] 
     rootCls     = 0x7fff86d48660 
     instanceObj = 0x600003e38170 
     classCls    = 0x10e5b4810 
     metaCls     = 0x10e5b47e8 
     rootMetaCls0 = 0x7fff86d48638 
     rootMetaCls1 = 0x7fff86d48638 
     rootMetaCls  = 0x7fff86d48638 
     rootMetaSuperCls = 0x7fff86d48660
    2021-05-04 16:55:11.688742+0800 ClassDemo[49442:9299385] 
     instanceObj-isa = 0x10e5b4810 
     classCls-isa    = 0x10e5b47e8 
     metaCls-isa     = 0x7fff86d48638 
     rootMetaCls0-isa = 0x7fff86d48638
    

    根据打印结果很明显:

    • instance对象的isa指向Class对象(0x10657a808, 第一条线)
    • Class对象的isa指向meta-class对象(0x10657a7e0, 第二条线)
    • meta-class的Superclass是root-meta-class (0x7fff86d48638, 第三条线)
    • meta-class的isa指向 root-meta-class (0x7fff86d48638, 第四条线)
    • root-meta-class的isa指向它自己 (0x7fff86d48638, 第五条线)
    • root-meta-class的Superclass是Rootclass (0x7fff86d48660)

    六. 拓展

    由于基元类的Superclass指向基类,我们会发现一个神奇的问题:如果一个类方法一直找不到,发现基类的对象方法存在,则会调用基类的对象方法,也就是子类的一个+方法,调用了基类的一个-方法。这是因为iOS的消息方法机制调用的时候没有区分对象方法和类方法,代码验证一下:
    定义一个NSObject类别,里面给NSObject加一个对象方法

    .h
    
    @interface NSObject (category)
    
    - (void)showName;
    
    @end
    
    .m 
    @implementation NSObject (category)
    
    - (void)showName {
        NSLog(@"NSObject (category) class = %@", NSStringFromClass([self class]));
    }
    @end
    
    

    Person继承自NSObject,在.h声明+ (void)showName方法不实现(为了编译器不报错,也可以用runtime发消息调用)

    .h
    @interface Person : NSObject
    + (void)showName;
    @end
    
    .m
    @implementation Person
    
    @end
    
    

    调用

    [Person showName];
    
    

    我们在调用Person+ (void)showName方法竟然执行了NSObject category里定义的- (void)showName方法

    2021-05-04 17:09:56.436515+0800 ClassDemo[51930:9318834] NSObject (category) class = Person
    

    这也很好的证明了基元类对象的Superclass指向基类

    相关文章

      网友评论

          本文标题:iOS - isa、superclass指针,元类supercl

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