runtime02 - isa指针

475 阅读3分钟

isa的底层结构

isa 是一个共用体, 共用体占用的内存大小, 跟它占据内存最大的成员变量相同, (isa一共是8字节)

在我们现在使用的arm64/arm64e的机器上,isa使用了共同体, 搭配位域, 来更合理的使用内存空间, isa存储的内容中, 除了对应class的地址外, 还存着其他的信息:

arm64e: arm64e 架构用于 A12 芯片组, iphoneX以上的机子都是arm64e

uintptr_t nonpointer: 是否使用了isa优化技术, 1 代表有, 0 代表没有
uintptr_t has_assoc:  是否使用过关联    , 1 代表有, 0 代表没有 (0的话, dealloc的时候会更快)
uintptr_t weakly_referenced:  是否被若指针引用过, 1 代表有, 0 代表没有 (0的话, dealloc的时候会更快)
uintptr_t shiftcls / shiftcls_and_sig:   class的地址
uintptr_t extra_rc:   引用计数
uintptr_t has_sidetable_rc: retain count是否超过extra_rc能放的空间, 1代表超过, 0代表没有

uintptr_t magic: 是否初始化成功(objc_object::initIsa中能看到,跟nonpointer一起赋的值)
uintptr_t has_cxx_dtor: 看起来像是是否有cpp析构函数(不知道啥玩意)
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    uintptr_t bits;

private:
    // Accessing the class requires custom ptrauth operations, so
    // force clients to go through setClass/getClass by making this
    // private.
    Class cls;

public:
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };

    bool isDeallocating() {
        return extra_rc == 0 && has_sidetable_rc == 0;
    }
    void setDeallocating() {
        extra_rc = 0;
        has_sidetable_rc = 0;
    }
#endif

    void setClass(Class cls, objc_object *obj);
    Class getClass(bool authenticated);
    Class getDecodedClass(bool authenticated);
};

// 64e
#   if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
#     define ISA_MASK        0x007ffffffffffff8ULL
#     define ISA_MAGIC_MASK  0x0000000000000001ULL
#     define ISA_MAGIC_VALUE 0x0000000000000001ULL
#     define ISA_HAS_CXX_DTOR_BIT 0
#     define ISA_BITFIELD                                                      \
        uintptr_t nonpointer        : 1;                                       \
        uintptr_t has_assoc         : 1;                                       \
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t shiftcls_and_sig  : 52;                                      \
        uintptr_t has_sidetable_rc  : 1;                                       \
        uintptr_t extra_rc          : 8
#     define RC_ONE   (1ULL<<56)
#     define RC_HALF  (1ULL<<7)
#   else
#     define ISA_MASK        0x0000000ffffffff8ULL
#     define ISA_MAGIC_MASK  0x000003f000000001ULL
#     define ISA_MAGIC_VALUE 0x000001a000000001ULL
#     define ISA_HAS_CXX_DTOR_BIT 1
#     define ISA_BITFIELD                                                      \
        uintptr_t nonpointer        : 1;                                       \
        uintptr_t has_assoc         : 1;                                       \
        uintptr_t has_cxx_dtor      : 1;                                       \
        uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
        uintptr_t magic             : 6;                                       \
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t unused            : 1;                                       \
        uintptr_t has_sidetable_rc  : 1;                                       \
        uintptr_t extra_rc          : 19
#     define RC_ONE   (1ULL<<45)
#     define RC_HALF  (1ULL<<18)
#   endif

位域是什么

  1. 为了节省空间, 结构体可以指定成员变量占据的位数, 当然占据的位数不能超过成员变量的类型最大可以占据的位数
  2. 最开始的成员变量, 从最右边开始占据跟位数相等的空间
  3. 利用位操作, 利用 & 操作, 就可以获取到每一位 或者 相邻若干位 的值
  4. 利用位操作, 利用 | 操作, 就可以设置每一位的值

描述一下对象,类,元类的isa指针指向

对象 -> 类 -> 元类 -> 根元类 -> 自己

类A只有+test的声明, 但没有实现+test方法, 如果NSObject实现了-test方法,当执行[A test]时会发生什么

会调用到NSObject-test方法中去, 因为类Aisa指针指向类A的元类, 根据消息发送机制, 会沿着类A的元类的superClass一路往上走, 因为根元类的父类是NSObject, 所以会执行NSObject-test方法

[[NSObject class] class] 返回的是NSObject的类对象, 还是NSObject的元类对象

是类对象, 因为类对象调用的+class方法, 返回是self

对象的isa指针是存着类对象的地址吗

不一定是, 64bit以后, 实例对象的isa的值, 需要&ISA_MASK后, 才是类对象所在的地址

核心图

image.png

DEMO

// xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main_arm64.cpp

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [NSObject new];

        Class cls1 = [obj class];
        Class cls2 = object_getClass(obj);
        Class cls3 = [NSObject class];
        Class cls4 = [[NSObject class] class]; // 这种还是类
        Class cls5 = objc_getClass("NSObject"); // 有一个hashmap, 根据传入的名称查找类

        Class metaCls1 = object_getClass(cls1);         // 查isa指针的指向
        Class metaCls2 = objc_getMetaClass("NSObject"); // 有一个hashmap, 根据传入的名称查找元类
        
        bool isMeta = class_isMetaClass(metaCls1);
        
        NSLog(@"%p %p %p %p %p", cls1, cls2, cls3, cls4, cls5);
        NSLog(@"%p %p", metaCls1, metaCls2);
        NSLog(@"%d", isMeta);
    }
    return 0;
}

// 2021-09-27 14:43:48.238769+0800 TestOC[51231:5399709] 0x7fff88960cc8 0x7fff88960cc8 0x7fff88960cc8 0x7fff88960cc8 0x7fff88960cc8
// 2021-09-27 14:43:48.239037+0800 TestOC[51231:5399709] 0x7fff88960ca0 0x7fff88960ca0
// 2021-09-27 14:43:48.239097+0800 TestOC[51231:5399709] 1

/* NSObject的class 方法实现
NSObject.mm {
    + (Class)class {
        return self;
    }

    - (Class)class {
        return object_getClass(self);
    }
}
*/

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

/***********************************************************************
* objc_getClass.  Return the id of the named class.  If the class does
* not exist, call _objc_classLoader and then objc_classHandler, either of
* which may create a new class.
* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
**********************************************************************/
//Class objc_getClass(const char *aClassName)
//{
//    if (!aClassName) return Nil;
//
//    // NO unconnected, YES class handler
//    return look_up_class(aClassName, NO, YES);
//}