iOS isa结构与类关联

2020-09-14  本文已影响0人  jokerlee

前言

了解isa之前首先看一下Clang

正文

探索对象

@interface Person : NSObject

@property (nonatomic , copy) NSString *name;

@end

@implementation Person

@end

通过Clang编译main.m,编译成main.cpp

//没有依赖的情况下 main.m
clang -rewrite-objc main.m -o main.cpp  
//存在依赖的情况下编译 ViewController.m
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.7.sdk ViewController.m

分析编译结果main.cpp当中发现Person被编译成了一个结构体
它的第一个属性就是isa

struct NSObject_IMPL {
    Class isa;
};

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_name;
};
///name
static NSString * _I_Person_name(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)); }
///属性赋值
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
///setName
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); }

可以发现在 NSObject_IMPL结构体中isa类型变成了class
我们在探索alloc&init&new当中有提到isa的初始化关联过程

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

所以苹果为了节省内存空间,这里使用了联合体的形式。
这里就是采用了联合体&位域的方式,我们来看看位域里边都存储了些什么

// arm64 真机
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   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 deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)
// x86_64 模拟器
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   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
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

arm64&x86_64分别对应的内存地址

isa64情况.jpeg
    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

接下来通过lldb

image.png

验证isa

1.通过位运算验证
2.通过& ISA_mask验证

第一种
image.png
因为shiftcls是偏移3个字节存入的 所以
将isa地址右移3位:p/x 0x001d800100002225 >> 3 ,得到0x0003b00020000444

在将得到的0x0003b00020000444左移20位:p/x 0x0003b00020000444 << 20 ,得到0x0002000044400000

为什么是左移20位?因为先右移了3位,相当于向右偏移了3位,而左边需要抹零的位数有17位,所以一共需要移动20
将得到的0x0002000041d00000再右移17位:po 0x0002000041d00000 >> 17 得到person

第二种
image.png
查看2进制下的0x0000000ffffffff8ULL
image.png
所以 字节&操作相当与摸出后3位和前面的字节
得到person
上一篇 下一篇

猜你喜欢

热点阅读