美文网首页
iOS isa结构与类关联

iOS isa结构与类关联

作者: jokerlee | 来源:发表于2020-09-14 19:27 被阅读0次

前言

了解isa之前首先看一下Clang

  • Clang是一个由Apple主导编写,基于LLVMC/C++/Objective-C编译器
  • 作用:借助Clang可以将oc文件输出成C++文件,方便探究其底层的一些结构逻辑底层的实现原理等。

正文

探索对象

  • main.m中有个一类Person ,当中有一个属性name
@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的初始化关联过程

  • isa相关
    联合体(union)一种特殊的数据类型,
    允许在相同的内存位置存储不同的数据类型。
    所有成员占用同一段内存,修改一个成员会影响其余所有成员
    共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉
    各变量是互斥的——缺点就是不够包容; 但优点是内存使用更为精细灵活,也节省了内存空间
    共用体占用的内存等于最长的成员占用的内存
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
  • nonpointer 表示是否对 isa 指针开启指针优化
    0:纯isa指针,1:不⽌是类对象地址,isa 中包含了类信息、对象的引⽤计数等
  • has_assoc 关联对象标志位,0没有,1存在
  • has_cxx_dtor 该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
  • shiftcls 存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
  • magic 用于调试器判断当前对象是真的对象还是没有初始化的空间
  • weakly_referenced 志对象是否被指向或者曾经指向一个 ARC 的弱变量,
    没有弱引用的对象可以更快释放。
  • deallocating 标志对象是否正在释放内存
  • has_sidetable_rc 当对象引用技术大于 10 时,则需要借用该变量存储进位
  • extra_rc 当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的 has_sidetable_rc。
    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);
    }
  • 转到initIsa
    initIsa
    可以发现cls类的信息就存储在isa通过偏移3个字节存入shiftcls

接下来通过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

相关文章

网友评论

      本文标题:iOS isa结构与类关联

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