[iOS]浅析isa指针

[iOS]浅析isa指针

文章目录

上一篇留的悬念不止分类的实现
还有isa指针到底是什么 它是怎么工作的
class方法又是怎么运作的
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
这里面的class又是何方神圣
话不多说 let's go!

isa指针

每个OC对象都含有一个isa指针,arm64之前,isa仅仅是一个指针,保存着对象或类对象内存地址,在arm64架构之后,Apple对isa进行了优化,变成了一个共用体(union)结构,同时使用位域来存储更多的信息

isa的结构

直接上源码看看

很显然isa是一个共用体而且其中两个互斥成员变量为

私有的 Class cls

公共的 uintptr_t bits

bits在不同架构和不同运行平台上有不同的定义

我们这里看看arm64e架构的真机下bits的定义

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

一张图解释它

isa的初始化

一切美好都有一个开始

objectivec 复制代码
    // 在alloc中将类和指针做绑定
    obj->initInstanceIsa(cls, hasCxxDtor);

然后是留下一个不错的初印象

objectivec 复制代码
inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
	//下方函数调用就是isa的初始过程
    initIsa(cls, true, hasCxxDtor);
}

还得有一次难忘的深入了解

objectivec 复制代码
inline void 
objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}

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_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        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;
    }

    // This write must be performed in a single store in some cases
    // (for example when realizing a class because other threads
    // may simultaneously try to use the class).
    // fixme use atomics here to guarantee single-store and to
    // guarantee memory order w.r.t. the class index table
    // ...but not too atomic because we don't want to hurt instantiation
    isa = newisa;
}

你了解到爱情本质

创建了一个 isa_t 类型的 newisa 实例, 对其做赋值操作

news.bits初始化时候只设置了nonpointer,magic两个部分,其余部分都没有进行设置,故值都为0

看到藏在最后的那一句
isa = newisa

回到初次见面的地方

isa_t 是一个联合体, 有两个成员变量一个是 bits, 还有一个 是 cls。我们知道 联合体 中各变量是互斥的, 它的优点是内存使用更为精细灵活。 所以,也就是说, isa_t 有两种初始化方式:

  • bits 被赋值, cls 没有值或者值被覆盖;
  • cls 被赋值, bits 没有值或者值被覆盖。

记起未曾发现过的点点滴滴

常见的assert()函数作用是如果它的条件返回错误,则终止程序执行

assert的作用是计算表达式 expression 如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行

注意事项

这是针对优化后的isa

也就是共用体中启用bits的那些non-pointer isa

贴一篇博客
Non-pointer isa

不想翻译了

好饿 想回去吃夜宵然后睡觉了

朱军 我相信你们的英语能力;)

What does this mean for my code?

Don't read obj->isa directly.

The compiler will complain if you do. Trust the Compiler. The Compiler is your friend. Use [obj class] or object_getClass(obj) instead

Don't write obj->isa directly

Use object_setClass() instead

If you override +allocWithZone

you may initialize your object's isa field to a "raw" isa pointer. If you do, no extra data will be stored in that isa field and you may suffer the slow path through code like retain/release. To enable these optimizations, instead set the isa field to zero (if it is not already) and then call object_setClass().

If you override retain/release to implement a custom inline retain count

consider removing that code in favor of the runtime's implementation

The 64-bit iOS simulator currently does not use non-pointer isa. Test your code on a real arm64 device.

核心观点就是 别再直接读写修改咱的isa指针啦

多多通过使用给的方法来判断类吧

或者照搬源码里面对isa的操作也是极好的

另外奥

目前64位的模拟器没有用到这个non-pointer isa技术

请在真机上测试叭

What does this mean for debugging?

The debugger knows how to decode the class from the isa field. You should not need to examine it directly in most cases.

You can run your code with environment variable OBJC_DISABLE_NONPOINTER_ISA=YES to disable non-pointer isa for all classes. If your code works with this set and fails without it, you may be incorrectly accessing an isa field directly somewhere.

If you are writing a debugger-like tool, the Objective-C runtime exports some variables to help decode isa fields. objc_debug_isa_class_mask describes which bits are the class pointer: (isa & class_mask) == class pointer. objc_debug_isa_magic_mask and objc_debug_isa_magic_value describe some bits that help distinguish valid isa fields from other invalid values: (isa & magic_mask) == magic_value for isa fields that are not raw class pointers. These variables may change in the future so do not use them in application code.`

debugger对non-pointer isa啥都懂

遇到问题多反思反思你自己哪里有问题
为什么总是你有问题 我都遇不到

然后你自己想写debugger的话 小心上面的变量

它们迟早被改动

相关推荐
恋猫de小郭5 小时前
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
flutter·ios·swiftui
网安墨雨9 小时前
iOS应用网络安全之HTTPS
web安全·ios·https
福大大架构师每日一题11 小时前
37.1 prometheus管理接口源码讲解
ios·iphone·prometheus
BangRaJun1 天前
LNCollectionView-替换幂率流体
算法·ios·设计
刘小哈哈哈1 天前
iOS 多个输入框弹出键盘处理
macos·ios·cocoa
靴子学长1 天前
iOS + watchOS Tourism App(含源码可简单复现)
mysql·ios·swiftui
一如初夏丿2 天前
xcode15 报错 does not contain ‘libarclite‘
ios·xcode
杨武博2 天前
ios 混合开发应用白屏问题
ios
BangRaJun2 天前
LNCollectionView
android·ios·objective-c
二流小码农3 天前
鸿蒙元服务项目实战:终结篇之备忘录搜索功能实现
android·ios·harmonyos