3. isa的初始化和指向(isa走位)

433 阅读10分钟

isa

isa到底是什么?在官方的解释中是这样的

Every object is connected to the run-time system through itsisa instance variable, inherited from the NSObject class.isa identifies the object's class; it points to a structurethat's compiled from the class definition. Through isa, anobject can find whatever information it needs at run timesuch asits place in the inheritance hierarchy, the size and structure ofits instance variables, and the location of the methodimplementations it can perform in response to messages.

翻译过来的意思就是 每个对象都通过从NSObject类继承的isa实例变量来连接到运行时系统。 它指向从类定义编译的结构。 通过isa,对象可以找到运行时所需的任何信息,例如其在继承层次结构中的位置,其实例变量的大小和结构以及可以响应消息执行的方法实现的位置。

isa的结构内存对齐&isa已经提到了,isa初始化如下

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {//现在都是nonpointer 所以走else
        isa.cls = cls;
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0);


#if SUPPORT_INDEXED_ISA //#define SUPPORT_INDEXED_ISA 0 也走else
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#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

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}

可见isa和类存在绑定关系newisa.shiftcls = (uintptr_t)cls >> 3,我们来验证一下

//_x86模式下
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8


(lldb) x/4gx p
0x102f7d410: 0x001d800100002535 0x0000000000000000
0x102f7d420: 0x0000000000000000 0x0000000000000000

// 第一个参数就是isa 打印isa的二进制信息
(lldb) p/t 0x001d800100002535
(long) $1 = 0b0000000000011101100000000000000100000000000000000010010100110101

// isa->shiftcls 占44位,前面被其他信息占了3位,后面被其他信息占了17位,
// 右移3位清空其他信息
(lldb) p/t $1 >> 3
(long) $4 = 0b0000000000000011101100000000000000100000000000000000010010100110
// 左移17位清空其他信息
(lldb) p/t $4 << 17
(long) $5 = 0b0110000000000000010000000000000000001001010011000000000000000000

//还原 得出 shiftcls
(lldb) p/t $5 >> 17
(long) $6 =      0b0000000000000000001100000000000000100000000000000000010010100110

// 类右移3位得到 isa->shiftcls
(lldb) p/t (uintptr_t)LGTeacher.class >> 3
(uintptr_t) $2 = 0b0000000000000000000000000000000000100000000000000000010010100110

lldb扩展: x/4gx p 打印p的4段内存信息。同理:x/6gx就是打印6段内存信息。 p/t 打印二进制信息 p/o 打印八进制信息 p/d 打印十进制信息 p/x 打印十六进制信息

可见$6$2后面44位都是00000000000000100000000000000000010010100110,验证了对象初始化的isa与class绑定关系。

有没有更加简单的方法来验证呢?

Class object_getClass(id obj){
    if (obj) return obj->getIsa();
    else return Nil;
}

inline Class objc_object::getIsa() {
    if (!isTaggedPointer()) return ISA();
}
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# else
#   error unknown architecture for packed isa
# endif
inline Class objc_object::ISA() {
    ...
    return (Class)(isa.bits & ISA_MASK);
    ...
}

关键代码(Class)(isa.bits & ISA_MASK),我们来验证一下

(lldb) x/4gx p
0x102f7d410: 0x001d800100002535 0x0000000000000000
0x102f7d420: 0x0000000000000000 0x0000000000000000
/通过对象的isa & ISA_MASK
(lldb) p/x 0x001d800100002535 & 0x00007ffffffffff8
(long) $9 = 0x0000000100002530
//打印类的地址
(lldb) p/x LGTeacher.class
(Class) $7 = 0x0000000100002530 LGTeacher

$7$9一模一样

isa的指向分析(isa走位)

通过lldb调试来查看isa的指向走位

LGTeacher  *p = [LGTeacher alloc];
(lldb) x/4gx p
0x102f7d410: 0x001d800100002535 0x0000000000000000
0x102f7d420: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d800100002535 & 0x00007ffffffffff8
(long) $11 = 0x0000000100002530
(lldb) po 0x0000000100002530
LGTeacher
  1. p对象的isa & ISA_MASK 获取到了类的信息。即isa 从类的实例对象p指向了类LGTeacher
(lldb) x/4gx $11
0x100002530: 0x001d800100002509 0x0000000100b34140
0x100002540: 0x00000001003d7290 0x0000000000000000
(lldb) p/x 0x001d800100002509 & 0x00007ffffffffff8
(long) $13 = 0x0000000100002508
(lldb) po $13
LGTeacher
  1. 通过类的isa & ISA_MASK 获取到了另外一个类的信息。即isa 从类LGTeacher又指向了类LGTeacher(其实这个类是我们LGTeachermetaClass元类)。编译器会把一个类作为它的元类的实例化对象来处理,就像一个对象从类实例化出来。
(lldb) x/4gx $13
0x100002508: 0x001d800100b340f1 0x0000000100b340f0
0x100002518: 0x0000000102100040 0x0000000300000007
(lldb) p/x 0x001d800100b340f1 & 0x00007ffffffffff8
(long) $14 = 0x0000000100b340f0
(lldb) po $14
NSObject
  1. 元类的isa & ISA_MASK 获取到了另外一个类NSObject的信息。即isa 从元类LGTeacher又指向了类NSObject
(lldb) x/4gx $14
0x100b340f0: 0x001d800100b340f1 0x0000000100b34140
0x100b34100: 0x0000000100f45ae0 0x0000000400000007
  1. 可以看到了第三步的isa与第四步的isa是同一片内存地址。

由此得出的isa的指向关系为:对象 -> 类 -> 元类 -> 根元类 -> 根元类

通过代码验证也是一样的

// NSObject实例对象
NSObject *object1 = [NSObject alloc];
// NSObject类
Class class = object_getClass(object1);
// NSObject元类
Class metaClass = object_getClass(class);
// NSObject根元类
Class rootMetaClass = object_getClass(metaClass);
// NSObject根根元类
Class rootRootMetaClass = object_getClass(rootMetaClass);
NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",object1,class,metaClass,rootMetaClass,rootRootMetaClass);

0x100500540 实例对象
0x7fff9aaeb118 类
0x7fff9aaeb0f0 元类
0x7fff9aaeb0f0 根元类
0x7fff9aaeb0f0 根根元类

isa走位

类的继承关系

LGTeacher->LGPerson->NSObject,类内存信息中的第二个地址指向是superClass(这个位置后面的章节会探索到)

(lldb) x/4gx LGTeacher.class
0x100002530: 0x001d800100002509 0x0000000100002580
0x100002540: 0x00000001003d7290 0x0000000000000000
(lldb) po 0x0000000100002580
LGPerson //LGTeacher的父类为LGPerson

(lldb) x/4gx 0x0000000100002580
0x100002580: 0x001d800100002559 0x0000000100b34140
0x100002590: 0x00000001003d7290 0x0000000000000000
(lldb) po 0x0000000100b34140
NSObject //LGPerson的父类为NSObject

(lldb) x/4gx 0x0000000100b34140
0x100b34140: 0x001d800100b340f1 0x0000000000000000
0x100b34150: 0x00000001021152a0 0x0000000200000003
(lldb) po 0x0000000000000000
<nil> //NSObject的父类为nil

结论:类的继承关系:LGTeacher->LGPerson->NSObject->nil

元类的继承关系

(lldb) x/4gx LGTeacher.class
0x100002530: 0x001d800100002509 0x0000000100002580
0x100002540: 0x00000001003d7290 0x0000000000000000
(lldb) p/x 0x001d800100002509 & 0x00007ffffffffff8
(long) $5 = 0x0000000100002508 //LGTeacher的元类
(lldb) x/4gx 0x0000000100002508
0x100002508: 0x001d800100b340f1 0x0000000100002558
0x100002518: 0x0000000100ff9500 0x0000000300000003

1.获取LGTeacher的元类的父类为0x0000000100002558

(lldb) x/4gx 0x0000000100002580 //LGPerson
0x100002580: 0x001d800100002559 0x0000000100b34140
0x100002590: 0x00000001003d7290 0x0000000000000000
(lldb) p/x 0x001d800100002559 & 0x00007ffffffffff8
(long) $6 = 0x0000000100002558 //LGPerson的元类
(lldb) po 0x0000000100002558
LGPerson

2.获取LGTeacher的父类的元类为0x0000000100002558,得出LGTeacher的元类->LGPerson的元类

(lldb) x/4gx 0x0000000100002558 //LGPerson的元类
0x100002558: 0x001d800100b340f1 0x0000000100b340f0
0x100002568: 0x0000000102200370 0x0000000300000003

3.获取LGPerson的元类的父类为0x0000000100b340f0

(lldb) x/4gx 0x0000000100b34140 //NSObjec
0x100b34140: 0x001d800100b340f1 0x0000000000000000
0x100b34150: 0x00000001021152a0 0x0000000200000003
(lldb) p/x 0x001d800100b340f1 & 0x00007ffffffffff8
(long) $8 = 0x0000000100b340f0 //NSObjec的元类
(lldb) po 0x0000000100b340f0
NSObject

4.获取LGPerson的父类的元类为0x0000000100b340f0,得出LGPerson的元类->NSObjec的元类

(lldb) x/4gx 0x0000000100b340f0 //NSObject的元类
0x100b340f0: 0x001d800100b340f1 0x0000000100b34140
0x100b34100: 0x00000001022008b0 0x0000000400000007
(lldb) po 0x0000000100b34140 //NSObject
NSObject

5.获取NSObjec的元类的父类为0x0000000100b34140,得出NSObjec的元类->NSObjec

(lldb) po 0x0000000000000000
<nil>

6.获取NSObjec的父类为0x0000000000000000,得出NSObjec->nil

结论:元类的继承关系:LGTeacher元类->LGPerson元类->NSObject元类->NSObject->nil

和苹果官方图一模一样

注意:虚线是isa走位 对象 -> 类 -> 元类 -> 根元类 -> 根元类
实线是继承关系,根元类的父类是NSObject,NSObject的父类是nil