OC对象探究03:isa 解析

提前准备

为了探究对象在底层的实现方式,此次使用clang来进行探究。相关语句clang -rewrite-objc main.m -o main.cpp ,在终端中进入工程目录main.m所在的路径中,执行上面的语句,就会生成一个main.cpp的C++文件,该文件就是main.m文件的底层实现方式。

main.cpp探究

通过clang之后的main.cpp 文件可以看到如下代码:

struct YKPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_name;
};

可以看出我们创建的YKPerson类在底部被编译成了YKPerson_IMPL的结构体,name属性成为结构体的属性。所以我们可以得到第一个结论,对象在底层被编译成了结构体,对象在底层是以结构体的形式存在,而NSObject_ IVARS是在内部默认全部都有的属性isa

联合体位域

在日常开发中少不了在类中声明一些布尔或其他类型的属性用于判断。为了更高的利用内存,我们可以合理使用联合体位域的方式来减少对内存的占用。

@property (nonatomic, assign) BOOL front;
@property (nonatomic, assign) BOOL back;
@property (nonatomic, assign) BOOL left;
@property (nonatomic, assign) BOOL right;
union {
    char bits;
    struct {
          char front : 1;
          char back  : 2;
          char left  : 3;
          char right : 4;
    }
} _direction;

例如以上代码,日常开发中一般会使用第一段代码声明(front,back,left,right)四个属性来做相应的方向判断,并且在相应的setter,getter方法中进行相应的操作,因为BOOL类型是4字节类型,所以它们需要占用4*4 = 16字节 = 128位,只是为了做一个方向判断时,这个内存占用量其实也是不少的。所以这个时候我们可以用到联合体位域
在第二段代码中,因为char类型的数据为1字节,所以总共占用了5字节。每一个属性后面的数字代表的是所在的位置(例如:0000 1111,第一位代表front,第二位代表back,第三位代表3,第四位代表right)。使用联合体位域时需要配合相应的方法来对联合体位域中的属性进行相应设置。

  • 结构体
    优点:所有的变量是“共存”的,全面;
    缺点:struct内存空间的分配是粗放的,不管用不用,全分配。
  • 联合体
    优点:各个变量是“互斥”的,内存使用更为精细灵活,节省了内存空间;
    缺点:不够“包容”

isa分析

之前我们已经分析了对象在alloc时调用instanceSize计算内存空间,calloc像系统申请内存,接下来就是讲开辟的内存与对象类进行关联。通过源码分析,调用initInstanceIsa后在方法initIsa中进行处理,因此initIsa为核心方法。在该方法中主要是对isa的赋值操作,接下来对该方法做主要分析。属性isaisa_t类型,在该方法中对联合体isa_t 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
}

以上为联合体isa_t的定义,属性clsbits 互斥,只有当nonpointer不存在时cls才会被赋值,当nonpointer存在时,讲给bit赋值一个宏定义的ISA_INDEX_MAGIC_VALUE 0x001d800000000001ULL。重点在ISA_BITFIELD,以下是以x86架构下的关于相关定义,并对内部结构进行解读。

# 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)
  • nonpointer :表示是否对isa指针开启指针优化,0:纯isa指针,1:不止是类对象地址,isa包含了类信息、对象的引用计数等
  • has_assoc : 关联对象标志位,0没有,1存在
  • has_cxx_dtor : 该对象是否有C++或者OBJC的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象
  • shiftcls :在x86架构下占用44位内存。存储类指针的值。开启指针优化的情况下,arm64架构下占用33位内存。
  • magic:用于调试器判断当前对象是真的对象还是没有初始化的空间。
  • weakly_referenced :指对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放。
  • deallocating:标志对象是否正在释放内存。
  • has_sidetable_rc :当对象引用计数大于10时,则需要借用该变量存储进位。
  • extra_rc:当表示该对象的引用计数时,实际上是引用计数值减1,例如,如果对象的引用计数为10,那么extra_rc为9.如果引用计数大于10,则需要使用到上面的has_sidetable_rc
LGPerson.png

在之前的文章中讲到对象调用alloc时,在_class_createInstanceFromZone 中与类进行关联,通过调试obj为返回结果,0x001d8001000020f1isa,我们将isa 进行位移计算,目的是得到isa_t 中的shiftcls,再以16进制打印传入的cls时我们发现此时的shiftcls就是我们的类,代表此时我们自定义的类已经被系统关联,并初始化了内存空间。

拓展 (clang)

  • clang -rewrite-objc main.m -o main.cpp 把目标文件编译成c++文件
  • UIKit报错问题
    • clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platfroms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m 通过找到当前版本Xcode下对应的SDK来进行生成,一般在头文件中包含UIKit时出现该问题
  • xcode安装的时候顺带安装了xcrun命令,xcrun命令在clang的基础上进行了 一些封装,要更好用一些
  • xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp (模拟器)
  • xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp (手机)

结论

从以上可以得到在调用initIsa时是将系统为类创建的内存以及设置一些类的信息并与当前类进行关联。所以在isa 中保存了类的所有信息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352