美文网首页
OC实例对象以及类对象

OC实例对象以及类对象

作者: 慢慢变好中 | 来源:发表于2019-06-01 23:06 被阅读0次

    id obj=[NSObject alloc]init];

    生成了一个obj实例对象,此对象的类型为id,编译器不做类型检查,但是其本身实际上为一个NSObject实例对象。

    id 为一个typedef的一个类型,原型为 struct objc_object * ,是一个指针,指向一个objc_object类型的结构体。

    所以obj为为一个指向objc_object结构体的一个指针。

    打开alloc方法的定义,得到

    + (id)alloc {

        return _objc_rootAlloc(self);

    };

    继续键入_objc_rootAlloc的定义:

    id _objc_rootAlloc(Class cls)

    {

        return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);

    };这里可以看到,NSObject在此处方法中是以一个Class类型被传入的,传入的是一个Class 类型的指针cls。

    打开Class的定义,发现:

    typedef struct objc_class *Class;说明Class也是一个指针,并且是一个指向struct objc_class的指针。

    此时打开objc_class结构体的定义,得到:

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

        }

        void setData( class_rw_t* newData) {

            bits.setData(newData);

        }

    ...................................

    };

    结构体objc_class继承自结构体objc_object,第一行就注释着Class  ISA;

    打开objc_object结构体的定义:

    struct objc_object {

    private:

        isa_t isa;

    public:

        Class ISA();

        Class getIsa();

    ................

    };

    这个结构体只是一个isa_t类型的 isa变量,以及对这个isa_t结构的操作函数。

    在原来的OC语言中,这个isa是一个Class类型的。在objc_object类型表示的一个实例变量中,是有这个Class类型的isa变量的,这个指针指向了这个实例所属的类。因为同一个类的不同实例有不同的数据,他们单独保存自己的数据是理所当然的,但是不同实例的方法是一样的,如果每个实例都拷贝一份类的方法,那么内存的开销是很大的,所以比较合适的方案是实例变量各自保存各自的数据,而方法应该由类保管,各个实例均采用类中保存的那一份方法储存。每个实例变量访问类中的方法的途径就是通过此Class类型的isa变量来访问对象所属的类,isa变量是类的地址。

    现在OC语言将其原来的Class类型转换为了一个union数据类型的isa_t类型,现在的isa变量已经不是具有单纯的储存类的地址信息的作用了。

    这里的指针变量是一个64位,占八个字节的变量,如果仅仅用来储存类的地址,某种意义上是浪费的。

    所以利用union共同体,在此union中塞入从前的Class isa的指针变量(这个变量会写出来,但是不会用到,它的意义就是起到易读的作用和提示),再在其中定义一个大小为64大小的struct结构体(不同的平台,可能指针的大小不一致,所以结构体大小可能不是64位的大小),并在结构体中此利用一个少有问津的C语言技术————位域,将这个64位的结构体划分为若干个占字节数不同的变量,不同的变量所代表的不同数量字节储存不同的信息。但是如此一来如何找到类的地址信息呢?此isa变量已经不是一个指针直接指向类的地址。

    我们利用实例方法class来看一看:

    打开实例方法class(而非类方法class方法),此方法返回的就是一个实例的类信息:

    - (Class)class{

        return object_getClass(self);

    };继续打开object_getClass函数的定义:

    Class object_getClass(id obj)

    {

        if(obj)return obj->getIsa();

        else return Nil;

    };原来,实例方法class实际调用的是一个getIsa()函数,返回查看上方objc_object结构的部分定义时,可发现此函数的身影。

    再次打开getIsa()函数的定义:

    inline Class 

    objc_object::getIsa() 

    {

        if (!isTaggedPointer()) return ISA();

        uintptr_t ptr = (uintptr_t)this;

        if (isExtTaggedPointer()) {

            uintptr_tslot = 

                (ptr >>_OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;

            return objc_tag_ext_classes[slot];

        }else{

            uintptr_tslot = 

                (ptr >>_OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;

            returnobjc_tag_classes[slot];

        }

    };可以看到第一行调用ISA(),此函数也是定义在objc_object函数中的,并且就在getISA()上方,再次查看ISA()函数定义:

    inline Class 

    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

    };

    可以看到,此处已经开始利用isa结构体(isa本身是个union结构,不同的平台使得isa可转化对应的struct不同),利用其中的不同位域信息来进行逻辑判断————“  if(isa.nonpointer)”,此nonpointer在结构体中占用1个字节,用来判断此isa变量是否是普通的pointer类型,如果是,则nonpointer为1,反之为0;需要注意的是,此isa变量是位于你所初始化实例变量中(实际为一个objc_object结构体,含有一个isa变量),其值是通过你实例化二步——1.alloc 2.init.两步赋值好的,用了一个上面所说的union结构体来存储了类的地址信息和其他相关信息,具体的步骤需要参考alloc方法的源码,此isa变量是否用isa_t类型来表示是alloc方法决定的,即是否将所属类的地址以特殊方式写进isa中。

    判断在此起到的作用是:

    一.如果使用了union类型的isa指针

    1.如果此isa为特殊的isa,是附加了信息的指针,在当前的某种平台下,则取isa变量结构体的indexcls部分,毫无疑问,此变量所代表的那若干位的信息指明了所属类的地址。uintptr_t 为typedef的usigned long类型,取到的值通过classForIndex()函数处理过后返回。

    2.否则是没有附加信息的指针,直接返回isa的值(实际为isa所代表的isa_t结构中唯一的成员量bits的值),此处需要将其转化为指针Class类型再返回。

    二.如果没有使用union类型的isa指针

    ISA_MASK为:0x00007ffffffffff8ULL

    则通过和ISA_MASK掩码进行按位于操作,进行特殊位置的取值,是64位中的4到48位的信息。得到所属类的地址。

    实例对象中有一个isa指针指向类对象,这个指针位于objc_object中;而objc_class结构体中也含有一个isa指针,这个指针是继承自objc_object得来的,所以可以知道,类也是一个对象,其中的isa指针指向了类对象的类,称为元类。

    类对象保存了实例对象的信息——实例方法等;而元类则保存了类对象的信息——类方法。

    故可知,实例对象通过isa指针找到保存在类对象当中的方法;类对象通过isa指针找到保存在元类中的类方法。

    相关文章

      网友评论

          本文标题:OC实例对象以及类对象

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