iOS—从源码分析retain、release、retainCount实现


内容会陆续补充

isa简单了解

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

  • 这里说一下struct和union的区别
  1. 两者都可以包含多个不同类型的数据,如int、double、Class等。
  2. 在struct中各成员有各自的内存空间,一个struct变量的内存总长度大于等于各成员内存长度之和;而在union中,各成员共享一段内存空间,一个union变量的内存总长度等于各成员中内存最长的那个成员的内存长度。
  3. 对struct中的成员进行赋值,不会影响其他成员的值;对union中的成员赋值时,每次只能给一个成员赋值,同时其它成员的值也就不存在了。

isa的数据结构其实是isa_t,是一个共用体(union修饰),意味着共用内存。
结构如下
在这里插入图片描述

isa的bits成员变量

isa的bits成员变量类型是uintptr_t,它实质上是个unsigned long,在64位架构下bits长度为64位,也就是8字节其中每一位的存储使用了位域,即ISA_BITFIELD。
在这里插入图片描述

  • 位域或位段
    一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。
    优点:
    节省储存空间;
    可以很方便的访问一个整数值的部分内容从而可以简化程序源代码。
    缺点:
    其内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,这导致了位域在本质上是不可移植的。

宏ISA_DITDIELD
关于这个宏定义在不同架构下的表示部分如下,这里只截取了部分,大概看一下就可,下面会详细介绍
在这里插入图片描述
在64位下,isa其实还是占8个字节内存,共64位个二进制位

  • 由于共用体特性, cls , bits 以及 struct 都是 8 字节内存 , 也就是说他们在内存中是完全重叠的。
  • 实际上在 runtime 中,任何对 struct 的操作和获取某些值,如 extra_rc,实际上都是通过对 bits 做位运算实现的。
  • bits 和 struct 的关系可以看做 : bits 向外提供了操作 struct 的接口,而 struct 本身则说明了 bits 中各个二进制位的定义。

以获取有无关联对象来举例 :

可以直接使用 isa.has_assoc , 也就是点语法直接访问 bits 中第二个二进制位中的数据 . ( arm 64 架构中 )
关于ISA_BITFIELD中每一个字段所存储的内容,我们以arm64为例

#     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

在这里插入图片描述
对于extra_rc存储的是引用计数-1的理解,它存储的是对象自身以外的引用计数,但随着源码的更新,创建对象时对isa的初始化中,对extra_rc初始化为1,所以extra_rc存储的内容直接就是对象的引用计数,这里我们不必纠结到底减不减1,只需要知道它存储的是对象的引用计数。

对于在不同的架构下,存储的数据没有变,只是占据位不同

isa的cls成员变量

在这里插入图片描述
在这里插入图片描述
由源码可知,cls是Class类型,而Class其实是指向objc_class结构体的指针变量,即cls就是指向objc_class结构体的指针变量。

总结

对于上面对isa结构的分析,我们可以知道isa其实分为两种
cls或bits
也就是

  • 纯指针,指向内存地址
  • NON_POINTER_ISA,除了内存地址,还存有一些其他信息

参考,所以isa的结构大致内容如下

union isa_t 
{
   
    Class cls;
    uintptr_t bits;
    struct {
   
         uintptr_t nonpointer        : 1;//->表示使用优化的isa指针
         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;//1->在extra_rc存储引用计数将要溢出的时候,借助Sidetable(散列表)存储引用计数,has_sidetable_rc设置成1
        uintptr_t extra_rc          : 19;  //->存储引用计数
    };
};


sideTables、sideTable 简单了解

sideTablesSideTables() 方法返回的是一个 StripedMap& 类型的引用:在这里插入图片描述

// StripedMap<T> is a map of void* -> T, sized appropriately 
// for cache-friendly lock striping. 
// For example, this may be used as StripedMap<spinlock_t>
// or as StripedMap<SomeStruct> where SomeStruct stores a spin lock.
template<typename T>
class StripedMap {
   
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    enum {
    StripeCount = 8 };
#else
    enum {
    StripeCount = 64 };
#endif

    struct PaddedT {
   
        T value alignas(CacheLineSize);
    };

    PaddedT array[StripeCount];
    ...
}

StripedMap 是一个模板类, 内部维护一个大小为 StripeCount 的数组, 数组的成员为结构体 PaddedT, PaddedT 结构体只有一个成员 value, value 的类型是 T.
StripedMap< SideTable >, 说明类内的结构体的成员类型为SideTable,根据源码中的注释可知StripedMap是一个void* -> T的映射。哈希值(value,即元素sideTable)可根据对象的地址(key)得到。

在这里插入图片描述


sideTable

struct SideTable {
   
    spinlock_t slock;
    RefcountMap refcnts;    //存引用计数的哈希表表
    weak_table_t weak_table;  //用于存储对象弱引用的哈希表
}
 

RefcountMap是一个hash map,其key是obj的DisguisedPtr<objc_object>。value,则是obj对象的引用计数,同时,这个map还有个加强版功能,当引用计数为0时,会自动将对象数据清除。

参考
参考


//
static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;


下面在retainCount中会有从sidetables、sideTable中获取value的步骤

retainCount实现


- (NSUInteger)retainCount {
   
    return _objc_rootRetainCount(self);
}

_objc_rootRetainCount(id obj)
{
   
    ASSERT(obj);

    return obj->rootRetainCount();
}


objc_object::rootRetainCount()
{
   
    //标记指针 直接返回
    if (isTaggedPointer()) return (uintptr_t)this;

    sidetable_lock();
    isa_t bits = __c11_atomic_load((_Atomic uintptr_t *)&isa.bits, __ATOMIC_RELAXED);
    
    //nonpointer 为1 表示优化过的isa指针
    if (bits.nonpointer) {
   
        uintptr_t rc = bits.extra_rc;
        
        //has_sidetable_rc 为1说明 extra_rc溢出,存放在sidetable中
        if (bits.has_sidetable_rc) {
   
            rc += sidetable_getExtraRC_nolock();
        }
        sidetable_unlock();
        return rc;
    }

    //未优化的isa指针
    sidetable_unlock();
    return sidetable_retainCount();
}

//优化过的isa,且extra溢出,要执行的方法
size_t 
objc_object::sidetable_getExtraRC_nolock(
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值