美文网首页
瞅一瞅、看一看、isa指针怎么办?

瞅一瞅、看一看、isa指针怎么办?

作者: 洲洲哥 | 来源:发表于2020-11-30 11:08 被阅读0次

    瞅一瞅、看一看、isa指针怎么办?

    关于这个问题,首先要了解异或、与、或运算。可以查看我往期的文章,有详细介绍。

    1:要搞清楚isa指针,必须要知道的几个变量

    // 简化了代码如下
    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL
    
    # elif __x86_64__
    #   define ISA_MASK        0x00007ffffffffff8ULL
    
    
    // 简化了代码如下
    struct objc_object {
    private:
        isa_t isa;
    public:
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA();
    
        // getIsa() allows this to be a tagged pointer object
        Class getIsa();
    };
    

    也就是比较简单的ISA_MASKisa。这里可以暂且先可以这样记住isa的指向问题。实例对象的isa指向类对象,类对象的isa指向元类,元类对象指向根元类,根元类指向自己。姑且先按下图的方式记住。

    isa和superclass的指向图

    2:用代码验证isa指针问题

    2.1 先添加两个类,代码实现如下
    @interface Person : NSObject
    
    @property (nonatomic, strong) NSString * personName;
    @property (nonatomic, strong) NSString * personAges;
    @property (nonatomic, strong) NSString * personAddrress;
    
    -(void)personIstanceMethod1;
    -(void)personlnstanceMethod2;
    -(void)personlnstanceMethod3;
    
    
    +(void)personClassMethod1;
    +(void)personClassMethod2;
    +(void)personClassMethod3;
    
    @end
    
    @interface Girl : Person
    @property (nonatomic, strong) NSString * girlName;
    @property (nonatomic, strong) NSString * girlAges;
    @property (nonatomic, strong) NSString * girlAddrress;
    
    -(void)girlnstanceMethod1;
    -(void)girlnstanceMethod2;
    -(void)girlnstanceMethod3;
    
    
    +(void)girClassMethod1;
    +(void)girClassMethod2;
    +(void)girClassMethod3;
    @end
    
    2.2 验证代码如下
    Girl * girls = [[Girl alloc] init];
    Class instanceClass = [girls class];
    Class clsClass = [Girl class];
    Class metaClass = object_getClass(clsClass);
    NSLog(@"--instanceClass:%p----clsClass:%p-----metaClass:%p",instanceClass,clsClass,metaClass);
    
    2.3 断点查看各对象的isa指针的值
    (lldb) p/x girls->isa
    (Class) $0 = 0x00000001067bd750 Girl
    (lldb) p/x instanceClass
    (Class) $1 = 0x00000001067bd750 Girl
    (lldb) p/x clsClass
    (Class) $2 = 0x00000001067bd750 Girl
    (lldb) p/x metaClass
    (Class) $3 = 0x00000001067bd778
    (lldb) p/x object_getClass(girls)
    (Class) $4 = 0x00000001067bd750 Girl
    

    这里尽然可以发现girls->isa指针地址和clsClass对象的isa地址是一样的。别着急,下面继续验证。

    这里意外的发现instanceClassclsClass对象地址是相同的。也就是说都是类对象。同等于[Girls class]
    顺便也可得出object_getClass返回对象的结论:

    如果传入的是实例对象返回的结果就类对象

    如果传入的是类对象返回的结果就元类对象

    2.4 言归正传话说ISA指针

    实例对象的isa指针地址是0x00000001067bd750

    在第一部分中关于objc_object源码中有一个私有变量

    private:
        isa_t isa;
    

    和他相关联的isa_t是一个联合体如下

    这里需要注意的是bits属性

    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    #if defined(ISA_BITFIELD)
        struct {
            ISA_BITFIELD;  // defined in isa.h
        };
    #endif
    };
    

    其中objc_object公开的关于isa的方法

    public:
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA();
    
        // getIsa() allows this to be a tagged pointer object
        Class getIsa();
    

    方法ISA()源码如下

    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
    }
    

    注意这里有个宏定义

    SUPPORT_INDEXED_ISA
    表示 isa_t 中存放的 Class 信息是 Class 的地址,还是一个索引(根据该索引可在类信息表中查找该类结构地址)。经测试,iOS 设备上 SUPPORT_INDEXED_ISA 是 0。

    TaggedPointer此处不做介绍

    最后这个ISA()执行到

    return (Class)(isa.bits & ISA_MASK);
    

    可以看到&ISA_MASK。接着我们会到2.4 断点查看各对象的isa指针的值小节。用girl的实例对象isa & ISA_MASK。如果过你忘了ISA_MASK是什么了,请翻到最上方可查看定义。
    代码如下

    p/x 0x00000001067bd750 & 0x0000000ffffffff8
    (long) $5 = 0x00000001067bd750
    

    会发现&之后没变。是不是感觉& ISA_MASK 是不是没用。

    记住这个问题后,在继续验证类对象的isa和元类对象的isa关系

    由于断点没法看Class对象的isa
    需要自定义一个结构体

    struct custom_objc_class {
        Class  isa;
    };
    

    看看校验代码

    Girl * girls = [[Girl alloc] init];
    
    Class instanceClass = [girls class];
    Class clsClass = [Girl class];
    struct custom_objc_class * _objc_class = (__bridge custom_objc_class  *)(clsClass);
    Class metaClass = object_getClass(clsClass);
    

    校验代码如下

    (lldb) p/x _objc_class->isa
    (Class) $0 = 0x000000010a2168c0
    (lldb) p/x metaClass
    (Class) $1 = 0x000000010a2168c0
    (lldb) p/x  0x000000010a2168c0 & 0x0000000ffffffff8
    (long) $2 = 0x000000010a2168c0
    

    第一眼没变化,是不是,类对象的isa和原来的地址是一样的。即使是&ISA_MASK了之后也没感觉。。。。。。

    各位看官请继续向下看!!!

    以上是我创建的iOS项目,为了验证问题,继续创建一个命令行项目。

    3:继续用命令行项目的代码验证isa指针问题

    3.1命令行项目验证实例对象的isa

    所有的步骤和创建的两个类都一样。就省略一下
    直接上运行的代码

    #import "Girl.h"
    #import <objc/runtime.h>
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            Girl * girls = [[Girl alloc] init];
            Class instanceClass = [girls class];
            Class clsClass = [Girl class];
            Class metaClass = object_getClass(clsClass);
            NSLog(@"--instanceClass:%p----clsClass:%p-----metaClass:%p",instanceClass,clsClass,metaClass);
        }
        return 0;
    }
    
    

    直接打印对象的地址和&ISA_MASK的操作

    (lldb) p/x girls->isa
    (Class) $0 = 0x001d8001000021d9 Girl
    (lldb) p/x clsClass
    (Class) $1 = 0x00000001000021d8 Girl
    (lldb) p/x  0x001d8001000021d9 &  0x00007ffffffffff8
    (long) $2 = 0x00000001000021d8
    

    注意这里跑的命令行项目。所有要用0x00007ffffffffff8值来做为ISA_MASK。

    3.2命令行项目验证类对象的isa

    和在iOS项目下一样的创建一个结构体

    struct custom_objc_class {
        Class  isa;
    };
    
    Girl * girls = [[Girl alloc] init];
    Class instanceClass = [girls class];
    Class clsClass = [Girl class];
    struct custom_objc_class * _objc_class = (__bridge custom_objc_class  *)(clsClass);
        
    Class metaClass = object_getClass(clsClass);
    

    直接打印输出如下

    (lldb) p/x cs_objc_class->isa
    (Class) $0 = 0x00000001000021b8
    (lldb) p/x metaClass
    (Class) $1 = 0x00000001000021b8
    (lldb) p/x 0x00007ffffffffff8 & 0x00000001000021b8
    (long) $2 = 0x00000001000021b8
    

    这个很奇怪哦,类对相关isa竟然和元类对象的地址是一样的。。。。是不是偶然的

    小结:
    isa和supper

    所以证明了实例对象的isa&ISA_MASK 可以得到类对象的地址,
    当然也可以同样的验证类的isa&ISA_MASK可以得到元类对象的地址。

    至于为什么能&ISA_MASK能得到对象的地址。本文暂不错解释,后面会有详细说明,可以先说一点点,这里iOS对arm64的isa指针做了优化。

    下一期讲实例对象和、类对象、元类中都存放了那些信息
    如果你觉得可以添加"洲洲哥“

    相关文章

      网友评论

          本文标题:瞅一瞅、看一看、isa指针怎么办?

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