Runtime源码剖析-对象
admin
2024-03-25 17:24:00
0
  • Runtime源码剖析-对象
    • 预备知识
      • Clang
        • 什么是Clang
        • 终端编译命令
    • 对象
      • 对象结构
      • struct objc_object
      • isa_t
        • ISA_BITFIELD
        • 总结
      • Isa_t初始流程
        • initInstanceIsa
        • initIsa
          • newisa.bits
          • newisa.has_cxx_dtor
          • newisa.setClass
          • newisa.extra_rc

Runtime源码剖析-对象

预备知识

  • 如果大家对联合体、位域相关知识不够熟悉的话,请参考联合体+位域

Clang

什么是Clang

Clang是一个C语言C++Objective-C语言的轻量级编译器。源代码发布于BSD协议下。Clang将支持其普通lambda表达式、返回类型的简化处理以及更好的处理constexpr关键字。Clang是一个有Apple主导编写,基于LLVMC/C++/Objective-C/Objective-C++编译器

  • 简单来说就是一个编译器,可以把我们写的OC代码,编译成C++代码。便于观察底层逻辑。

终端编译命令

  1. 首先需要在main.m文件中,创建一个Person类,然后在main函数中,创建一个对象
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end@implementation Person@endint main(int argc, const char * argv[]) {@autoreleasepool {Person *person1 = [[Person alloc] init];}return 0;
}
  1. 然后使用clang命令,把.m文件转换成.cpp文件
// 1. 通过clang命令
clang -rewrite-objc main.m -o main.cpp // 编译引入 UIKit的文件
// UIKit报错
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m// 2. 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 
  • 执行完后,会在对应目录下生成.cpp文件

对象

对象结构

  • 打开.cpp文件,我们想要知道对象的结构,于是我们先去寻找main方法,因为在oc中,我们在这个方法中创建了一个Person对象。
int main(int argc, const char * argv[]) {/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; Person *person1 = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));}return 0;
}
  • 然后我们发现最终创建了一个Person类型的指针,引用着Person对象。接着我们去查询Person的定义
#ifndef _REWRITER_typedef_Person
#define _REWRITER_typedef_Person
typedef struct objc_object Person;
typedef struct {} _objc_exc_Person;
#endif
  • 发现其实Person对象,就是一个objc_object类型的结构体,我们接着去查询objc_object的定义
typedef struct objc_class *Class;struct objc_object {Class _Nonnull isa __attribute__((deprecated));
};
  • 这个时候发现,原来它内部包含了一个Class类型的指针,也就是说Person对象内部只有一个指针。本以为我们已经接近真相了,突然发现__attribute__((deprecated))这个标识符,通过查询它表示该变量已经被废弃。也就是说其实objc_object内部是空的什么也没有。我们现在已经得知对象就是一个struct objc_object类型的结构体,现在想要知道它内部是怎么实现的。这个时候通过苹果开源的OBJC4源码中,去查找对应的结构。

struct objc_object

struct objc_object {
private:isa_t isa;
public:// 此处省略方法
};
  • 可以看到除了一些公开方法外,只有一个成员变量,类型为isa_t的变量

isa_t

union isa_t {isa_t() { }isa_t(uintptr_t value) : bits(value) { }uintptr_t bits;
private:Class cls;public:
#if defined(ISA_BITFIELD)struct {ISA_BITFIELD;  // defined in isa.h};// 省略此处方法
#endif// 省略此处方法
};
  • 此处isa_t是一个联合体,如果对这一块知识不够数序的,可以查看本文预备知识联合体+位域
  • 它一共有两个成员变量bitscls,共同占用这一段内存空间,由于它们是互斥的,同时只能使用其中一个变量
  • isa_t中还有一个结构体,其中ISA_BITFIELD位域信息,表述了bits的每一位的信息。所以本质上isa_t是一个联合体位域。下面我们看一下具体位域信息

ISA_BITFIELD

  • 点击这个宏,你发现它对应两个版本。一个是__arm64__(iOS真机+模拟器),一个是__x86_64__(macOS)。这里我们就选择arm64真机版本来查看
#     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)
  • 下面我们看一下具体的存储地址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBKSVxX7-1670416578566)(http://ww4.sinaimg.cn/large/006y8mN6ly1g67nqwjw3aj31900u0q4r.jpg)]

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

总结

  • isa_t分为nonpointer类型和非nonpointer。非nonpointer类型只是一个纯指针,nonpointer还包含了类的信息。什么是nonpointer和非nonpointer,参考Non-pointer isa

  • isa_t联合体+位域的方式存储信息的。采用这种方式的有点就是节省大量内存。通过位域的方式,可以在isa上面存储更多相关信息,内存得到充分的利用

Isa_t初始流程

  • 在Runtime源码剖析-alloc我们讲到alloc主要干了三件事,最后一件事就是把开辟的内存和类联系起来,具体就是通过initInstanceIsa达到这个目的。下面我们就来看看具体这个方法做了什么操作。

initInstanceIsa

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{ASSERT(!cls->instancesRequireRawIsa());ASSERT(hasCxxDtor == cls->hasCxxDtor());initIsa(cls, true, hasCxxDtor);
}
  • 该方法中调用了initIsa方法

initIsa

inline void 
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ ASSERT(!isTaggedPointer()); isa_t newisa(0);if (!nonpointer) {newisa.setClass(cls, this);} else {ASSERT(!DisableNonpointerIsa);ASSERT(!cls->instancesRequireRawIsa());#if SUPPORT_INDEXED_ISAASSERT(cls->classArrayIndex() > 0);newisa.bits = ISA_INDEX_MAGIC_VALUE;// isa.magic is part of ISA_MAGIC_VALUE// isa.nonpointer is part of ISA_MAGIC_VALUEnewisa.has_cxx_dtor = hasCxxDtor;newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#elsenewisa.bits = ISA_MAGIC_VALUE;// isa.magic is part of ISA_MAGIC_VALUE// isa.nonpointer is part of ISA_MAGIC_VALUE
#   if ISA_HAS_CXX_DTOR_BITnewisa.has_cxx_dtor = hasCxxDtor;
#   endifnewisa.setClass(cls, this);
#endifnewisa.extra_rc = 1;}isa = newisa;
}
  • 在这个方法里面,首先判断是否是nonpointer,如不是的话,说明isa_t就是一个指针,直接把把类地址传给他。如果是的话,说明isa_t是一个联合体位域,里面不仅仅包含类地址,还有一些别的信息。

  • 接着我们遇到一个宏定义SUPPORT_INDEXED_ISA,简单来说就是watch手表使用的架构,则该宏定义为1,其他架构为0。具体该宏的定义可以查看此篇文章SUPPORT_INDEXED_ISA

  • 由于我们研究的目的是在iOS上应用,所以此处宏定义为0,我们走下面这块代码

newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
#   if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
#   endif
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
  • 可以发现我们初始化就只有4步了,由于这里是在mac平台下的调试,所以这里以__x86_64__平台下isa_t位域来进行初始化,arm64初始化结构类似。
newisa.bits
  • 我们来看看ISA_MAGIC_VALUE的定义
#define ISA_MAGIC_VALUE 0x001d800000000001ULL
二进制表示:11101100000000000000000000000000000000000000000000001
  • 我们转换成二进制数据,然后看一下哪些属性对应的位域被这行代码初始化了(标记为红色)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xBnDfsop-1670416578567)(http://ww2.sinaimg.cn/large/006y8mN6ly1g67o401xshj31cc0rkwgq.jpg)]

  • 从图中了解到,在使用 ISA_MAGIC_VALUE 设置 isa_t 结构体之后,实际上只是设置了 nonpointer 以及 magic 这两部分的值。
  • magic 的值为 0x3b 用于调试器判断当前对象是真的对象还是没有初始化的空间
newisa.has_cxx_dtor
  • has_cxx_dtor表示当前对象有 C++ 或者 ObjC 的析构器(destructor),如果有析构器就会快速释放内存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ppyro9Xw-1670416578567)(http://ww2.sinaimg.cn/large/006y8mN6ly1g67oeaqxwqj31cc0rkq56.jpg)]

newisa.setClass
  • 这一步主要是把类的地址存下来
inline void
isa_t::setClass(Class newCls, UNUSED_WITHOUT_PTRAUTH objc_object *obj)
{// Match the conditional in isa.h.
#if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
#   if ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_NONE// No signing, just use the raw pointer.uintptr_t signedCls = (uintptr_t)newCls;#   elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ONLY_SWIFT// We're only signing Swift classes. Non-Swift classes just use// the raw pointeruintptr_t signedCls = (uintptr_t)newCls;if (newCls->isSwiftStable())signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));#   elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ALL// We're signing everythinguintptr_t signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));#   else
#       error Unknown isa signing mode.
#   endifshiftcls_and_sig = signedCls >> 3;#elif SUPPORT_INDEXED_ISA// Indexed isa only uses this method to set a raw pointer class.// Setting an indexed class is handled separately.cls = newCls;#else // Nonpointer isa, no ptrauthshiftcls = (uintptr_t)newCls >> 3;
#endif
}
  • 通过断点调试,可知最终走的是shiftcls = (uintptr_t)newCls >> 3;
  • 这里为什么需要把类的地址右移三位?
    • MACH_VM_MAX_ADDRESS表示虚拟内存最大寻址空间,在__arm64__MACH_VM_MAX_ADDRESS = 0x1000000000 虚拟内存最大寻址空间是36位。在__x86_64__MACH_VM_MAX_ADDRESS = 0x7fffffe00000 虚拟内存最大寻址空间是47位。
    • 字节对齐是8字节对齐,也就是说指针的地址只能是8的倍数,那么指针地址的后3位只能是0
    • 为了节省内存空间,把后3位0抹去
  • 地址填进去后,位域变化如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ij7nx9Xy-1670416578567)(http://ww3.sinaimg.cn/large/006y8mN6ly1g67ok5dzvzj31cc0rk0uz.jpg)]

newisa.extra_rc
  • 表示该对象的引用计数为1

相关内容

热门资讯

学生会查寝收走了东西,要用什么... 学生会查寝收走了东西,要用什么法律武器把收走的东西要回来?和学校讲法律,劝你还是别这么冲动了。拿到明...
信任无价主要讲了一个什么故事 信任无价主要讲了一个什么故事主要讲了:一位女老师通过绝食的办法,让那位偷了快译通的学生自己主动交出。
什么叫奴隶社会 什么叫奴隶社会 奴隶社会:从公元前21世纪夏朝建立开始,到公元前221年秦王嬴政统一中国结束。[1...
中级经济师人力资源专业怎么才能... 中级经济师人力资源专业怎么才能考过啊?只是看指定的那两本书《经济基础》和《人力资源管理》,然后再把上...
为什么海里都是水啊 为什么海里都是水啊海里没水 海就不叫海了不难怎么叫海水 嘿嘿。海里没水 海就不叫海了
14岁两个女生,一米深的河里手... 14岁两个女生,一米深的河里手绑手溺亡,疑点重重,怎么回事?这个是因为她们忍受不了家里面的重男轻女,...
帮解几道五年级数学题!急急急~... 帮解几道五年级数学题!急急急~快快快!(要解)52=2*2*13
问候幽默语 问候幽默语问候语幽默就应该问他开心快乐每一天,今天岁数是过的,又是非常的快乐的一天,非常充实。你现在...
想来其中自多情,你可知我意难平... 想来其中自多情,你可知我意难平何解!跟你说这句话的人想告诉你,他对你很用心用情,可是你却不在意,或许...
火影忍者所有op对应的集数 火影忍者所有op对应的集数一共9+20首。Naruto:“R★O★C★K★S”,第1至25集。“遥か...
求一本女主角很美好的名著 求一本女主角很美好的名著希望女主角内向些 大家认识的最善良 甜美的女主角 都推荐给我吧书最好是名著 ...
书名中带有 学生 的小说 书名中带有 学生 的小说富的生命。 “美”是生活的更新者,元气之恢复,健康之促进者,甚至可以说是生机...
开在上海乐高乐园里的比亚迪驾驶... ①首家:比亚迪与上海乐高乐园度假区达成的是战略合作伙伴关系,也是乐高乐园在全球合作的首个中国汽车品牌...
原创 林... 自从步入婚姻殿堂、迎来小宝贝后,林志玲的工作节奏悄然放慢,感觉她就像夏日的蜗牛,慢悠悠地享受生活。不...
武汉口碑最好的旅行社排名,靠谱... 在武汉这座充满魅力的城市,旅游市场蓬勃发展,旅行社众多,让人眼花缭乱。对于计划出游的朋友来说,选择一...
我是上海人,去了趟天津,有6点... 作为一个土生土长的上海人,我一直对北方城市充满好奇。上个月趁着年假,我独自踏上了天津之旅。本以为同为...
2025中国(青岛)-东盟经贸... 7月3日至5日,以“蓝色伙伴共赢未来”为主题的2025中国(青岛)-东盟经贸合作与人文交流活动在青岛...
开业首日600余人涌入“桃花源... “桃花源漂流”将速度与清凉完美融合。 连日高温,催热避暑经济,2025建德“17℃夏日漂流季”7月...
中盛旅投文化发展(北京)有限公... 中盛旅投文化发展(北京)有限公司:自然与现代交融 在这个世界上,自然与现代的交融总是能创造出令人惊叹...
在哈利波特与混血王子中,为什么... 在哈利波特与混血王子中,为什么罗恩在吃饭时自己头上会下雪才不是,才不是因为巴不得分手高兴所以挥舞魔杖...