美文网首页
内存管理 之 引用计数的存储

内存管理 之 引用计数的存储

作者: ychen3022 | 来源:发表于2019-01-15 16:13 被阅读11次
1、引用计数

在讲MRC、ARC的时候,我们知道是使用引用计数(retainCount)来管理OC对象的内存的。
那引用计数这个值又是存储在哪里的呢?
带着这个问题,我们往下看。

2、isa的结构

我们要知道,在OC语言的内部,每个对象都有一个isa指针,指向该对象的类。

#pragma -mark NSObject的结构

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

那么这个isa里面有哪些内容呢?

#pragma -mark 只看arm64情况下

struct {
        uintptr_t nonpointer        : 1;//0代表普通指针,存贮着class、meta-class对象的内存地址;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; //标记引用计数是否过大,无法存在isa中,如果为1,则表示引用计数存在一个sideTable表中
        uintptr_t extra_rc          : 19;//这里面存储的值是引用计数-1
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };  
3、引用计数存储在哪

从上面isa的结构内容中,我们可以看出引用计数的存储肯定是和isa有关的。
一般都是通过retainCount这个方法来获取某个对象的引用计数的,那我们就通过retainCount的底层实现来看看引用计数到底是怎么得到的。

//获取引用计数的方法
- (NSUInteger)retainCount {
    return ((id)self)->rootRetainCount();//调用rootRetainCount方法
}


//C++函数
inline uintptr_t
objc_object::rootRetainCount()
{
    //如果是taggedPointer ,那么就返回自己
    //因为如果你是个Tagged Pointer的话,那你就不是OC对象,你就是个普通的指针
    if (isTaggedPointer()) return (uintptr_t)this;

    sidetable_lock();
    isa_t bits = LoadExclusive(&isa.bits);//isa.bits其实就是isa本身内容
    ClearExclusive(&isa.bits);
    if (bits.nonpointer) {//判断非指针类型,如果是个优化过的isa指针,引用计数就为isa中的extra_rc + 1
        uintptr_t rc = 1 + bits.extra_rc;
        if (bits.has_sidetable_rc) {//判断一下是否是存在sideTable里,如果为1的话,不是存贮在isa中,是存在sideTable中
            rc += sidetable_getExtraRC_nolock();
        }
        sidetable_unlock();
        return rc;
    }

    sidetable_unlock();
    return sidetable_retainCount();//这个方法基本和上面sidetable_getExtraRC_nolock差不多,都是去sideTable去取值
}


//在sideTable中取引用计数
size_t 
objc_object::sidetable_getExtraRC_nolock()
{
    assert(isa.nonpointer);
    SideTable& table = SideTables()[this];//根据this这个key值去取一个value,这个this指的是对象本身
    RefcountMap::iterator it = table.refcnts.find(this);//在散列表SideTable中的refcnts中去取这个引用计数
    if (it == table.refcnts.end()) return 0;
    else return it->second >> SIDE_TABLE_RC_SHIFT;
}

从源码可以看出,获取retainCount的逻辑基本是:

  • 判断是否为Tagged Pointer:为Tagged Pointer则没有引用计数
  • 判断nonpointer是否为1,为1则表明优化过,存贮着更多信息(extra_rc中便存储着引用计数)。但是优化过的isa也并不一定就存储这引用计数,因为19bit保存引用计数不一定够。
  • 还要判断has_sidetable_rc是否为1。为1表明引用计数存在SideTable 的类的属性中。
4、散列表
屏幕快照 2019-01-15 下午3.11.36.png

SideTable 这个类,它用于管理引用计数表和后面将要提到的 weak 表,并使用 spinlock_lock 自旋锁来防止操作表结构时可能的竞态条件。
其中refcnts对应的作用就是管理引用计数。

5、总结

在64bit中,引用计数可以直接存储在优化过的isa指针中,也可能存储在SideTable类中。
如果对象地址为一个Tagged Pointer,那么则没有引用计数,
如果为一个优化过的指针且has_sidetable_rc为0,则引用计数存储在isa结构的extra_rc中。
其他情况下,基本是引用计数存在sideTable中的refcnts中。

相关文章

  • Obj-C高级编程--内存管理

    自动引用计数 自动引用计数:指内存管理中对引用采取自动计数的技术。 内存管理/引用计数 持有对象引起引用计数加...

  • OC内存管理--引用计数器

    原文链接OC内存管理--引用计数器 引用计数的存储策略 有些对象如果支持使用TaggedPointer,苹果会直接...

  • python内存管理

    python内存管理是通过引用计数来实现的。当对象的引用计数为0时,会被gc回收。 为了探索对象在内存的存储,我们...

  • 内存管理与自动引用计数—第一日

    自动引用计数 1.1 什么是自动引用计数 内存管理中对引用采用自动计数的计数 1.2 内存管理/引用计数 这一张举...

  • Effective Objective-C 2.0笔记(四)

    第五章 内存管理 第29条:理解引用计数 OC使用引用计数管理内存,引用计数机制通过递增递减的计数器来管理内存。对...

  • iOS内存管理1:引用计数

    iOS内存管理1:引用计数 引用计数: Objecttive-C使用引用计数来进行内存管理。然后,引用计数其实是不...

  • 内存管理-引用计数的存储

    在iOS中,内存管理是通过引用计数来管理的,那么对象的引用计数值存储在哪里?在Runtime(一)中介绍了isa指...

  • 内存管理 之 引用计数的存储

    1、引用计数 在讲MRC、ARC的时候,我们知道是使用引用计数(retainCount)来管理OC对象的内存的。那...

  • EffectiveObjective-C2.0 笔记 - 第五部

    5 内存管理 5.1 理解引用计数 1、引用计数 Objective-C 语言使用引用计数来管理内存,每个对象都有...

  • Objective-C高级编程之内存管理篇

    iOS的内存管理是采用引用计数的方式,引用计数分为手动引用计数和自动引用计数(ARC)。前者要求开发者手动管理内存...

网友评论

      本文标题:内存管理 之 引用计数的存储

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