iOS底层原理之isa分析

1.isa本质

我们知道,oc中的一切类都继承自NSObject,直接追踪NSObject可以发现在objc/NSObject.h文件中对于该类的定义如下

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

或者在objc源码中找到关于类在C语言层面的定义

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
  • 不难发现,每一个类的初始属性都是一个名为isa的指针,可以看出,isa的类型为Class,而Class则代表着一切类,继续查看Class的类型,发现Class其实是一个名为object_class的结构体指针,里面定义一些常用成员,所以初步得出isa就是一个类指针,里面存放着类的地址
typedef struct objc_class *Class;
  • 继续追踪objc_class,发现其继承自obj_object,实际上在旧版的代码中,objc_class是一个单纯的结构体,在new版本的代码中才继承自obj_object,这也符合了面向对象中,万物皆对象的理念。在objc-runtime-new.h文件中可以看到,之后寻找objc_object的初始定义,最终在objc-private.h文件中发现了最初始的object定义以及isa的详细结构,isa是一个联合体位域,里面关联了它所指向的类,也就是Class cls
struct objc_class : objc_object {
    // 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

    class_rw_t *data() { 
        return bits.data();
    }
}
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
};


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();
}

2.isa初始化

  • 在之前的alloc流程中,我们发现,在执行class_createInstance方法的时候,会调用 obj->initIsa(cls) 方法进行isa的初始化,其中cls代表当前的类,
  • 当nonpointer = 0,代表当前为普通指针,那么直接通过isa.cls和类进行关联
  • 当nonpointer = 1的时候,代表是优化过的isa指针,会使用位域存放更多的信息,此时创建newisa并初始化值后赋值给isa指针
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa.cls = cls;
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
    // 此处有一些代码,不过不重要,暂时不会走这里
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        isa = newisa;
    }
}

3.isa对类和对象的关联

  • 下面探索一下,isa是否已经和当前的类进行了关联,先通过object_getClass方法得出结论,获取对象的类其实就是返回当前对象的isa指针,那么就可以通过isa指针找出所属的类
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

objc_object::getIsa() 
{
    // 一般都不是TaggedPointer,这是特殊指针
    if (!isTaggedPointer()) return 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
}
// 64位架构下 ISA_MASK的值为
#   define ISA_MASK        0x00007ffffffffff8ULL
  • 进行试验,先通过x/4gx打印出当前对象的地址以及值,其中第一个位置必然为isa指针,那么就可以用p/x先输出类的地址,再通过isa & ISA_MASK找出isa对应的类地址,进行比对发现两者一致,所以得出结论isa关联了对象和类,对象的isa指针指向该对象所属的类,流程记录如下图

    image.png

4.类中isa的指向

    1. 刚才验证了isa对于类和对象的关联,由于class继承自object,万物皆Object,那么继续刚才的试验,验证一下类中的isa指向,废话不多说,直接上图
    image.png

问题来了,按照刚才的步骤打印出了类的地址,但是通过isa指针寻找之后得到地址0x0000000100001108,并且打印值和之前打印类一样均为LGPerson,但是二者的地址却截然不同,我们知道,类在内存中只可能存在一份,对象可以存在多份,那么可以推断,当前0x0000000100001108是一个和LGPerson名字相同但是却不一样的类,姑且认为它是 LG-A

    1. 继续按照步骤验证下去,终于发现值变了,此次isa指针寻找到的类发生了变化,新找到的类名为NSObject,但是任然存在刚才的问题,这个NSObject和我们直接打印的NSObject的地址并不相同,所以暂定它为NSObject-A

      image.png
    1. 继续重复步骤,发现最终NSObject-A类的isa指针一直指向自己,并且还发现有意思的是,第二个指针指向了NSObject类,其实这第二个指针就是类中的supercClass指针,所以NSObject-A类的父类其实就是NSObject类。
    image.png

总结:

  • 到此为止isa的本质,初始化以及isa在对象,类中的指向图已经分析完毕,在我们分析中所提到的NSObject-A,NSObject-A其实又叫做元类,其中NSObject的元类又叫做根源类,它是一切元类的父类,所有元类的isa指针全部指向根源类,最后回归到万物皆对象,所以根源类的父类则是NSObject,而NSObject的父类则为nil,这里本来应该有一副很经典的isa和父类继承路线图,但是可能对于很多人来说理解不是很容易,所以我上了一个汉化版本
    image.png

你可能感兴趣的:(iOS底层原理之isa分析)