iOS内存管理引用计数示例分析
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>内存管理机制</li><ul class="second_class_ul"><li>isa</li><li>SideTable</li></ul></ul></div><p class="maodian"></p><h2>内存管理机制</h2><div class="cros igoods"><div class="goodsin" data-img="https://img14.360buyimg.com/pop/jfs/t6586/118/178402578/56244/59c7902e/593acd1cNb1b001a9.jpg" data-name="iOS开发指南 从Hello World到App Store上架 第5版(图灵出品)" data-owner="京东自营" data-price="119" data-tgid="38" data-url="https://union-click.jd.com/jdc?e=&p=JF8BAL4JK1olXDYCV19VDkkSAV9MRANLAjZbERscSkAJHTdNTwcKBlMdBgABFksUAmcOGV4XQl9HCANteioVfWtYXzN1JnhrIicOY05kfG1ATVcZbQEHU1tVCk4UM28LHVwVXAMCZG5dCXtBbW8JGloUXAMGU1ltCXsXBGkLE1wSWQ4HUV9dOEsfB19LTx5BHkVKZG5tC3snM284GGtLMwdRUlxcWx9FbTJaQB4QWURAOl5dCEoTA20BGmsXXAcAVm5t"></div></div>
<p>目前流行的内存管理机制主要有<code>GC</code>和<code>RC</code>两种。</p>
<ul><li><code>GC</code> (Garbage Collection):垃圾回收机制,定期查找不再使用的对象,释放对象占用的内存。</li><li><code>RC</code> (Reference Counting):引用计数机制。采用引用计数来管理对象的内存,当需要持有一个对象时,使它的引用计数 +1;当不需要持有一个对象的时候,使它的引用计数 -1;当一个对象的引用计数为 0,该对象就会被销毁。</li></ul>
<p><code>Objective-C</code>支持三种内存管理机制:<code>ARC</code>、<code>MRC</code>和<code>GC</code>,但<code>Objective-C</code>的<code>GC</code>机制有平台局限性,仅限于<code>MacOS</code>开发中,<code>iOS</code>开发用的是<code>RC</code>机制,从<code>MRC</code>到现在的<code>ARC</code>。</p>
<p>一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间</p>
<p>调用<code>retain</code>会让OC对象的引用计数+1,调用<code>release</code>会让OC对象的引用计数-1</p>
<p>内存管理的经验总结</p>
<ul><li>当调用<code>alloc</code>、<code>new</code>、<code>copy</code>、<code>mutableCopy</code>方法返回了一个对象,在不需要这个对象时,要调用<code>release</code>或者<code>autorelease</code>来释放它</li><li>想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1</li><li>可以通过以下私有函数来查看自动释放池的情况</li></ul>
<blockquote><p>extern void _objc_autoreleasePoolPrint(void);</p></blockquote>
<p>以上我们对 “引用计数” 这一概念做了初步了解,Objective-C 中的 “对象” 通过引用计数功能来管理它的内存生命周期。那么,对象的引用计数是如何存储的呢?它存储在哪个数据结构里?</p>
<p>首先,不得不提一下<code>isa</code>。</p>
<p class="maodian"></p><h3>isa</h3>
<ul><li><code>isa</code>指针用来维护 “对象” 和 “类” 之间的关系,并确保对象和类能够通过<code>isa</code>指针找到对应的方法、实例变量、属性、协议等;</li><li>在 arm64 架构之前,<code>isa</code>就是一个普通的指针,直接指向<code>objc_class</code>,存储着<code>Class</code>、<code>Meta-Class</code>对象的内存地址。<code>instance</code>对象的<code>isa</code>指向<code>class</code>对象,<code>class</code>对象的<code>isa</code>指向<code>meta-class</code>对象;</li><li>从 arm64 架构开始,对<code>isa</code>进行了优化,用<code>nonpointer</code>表示,变成了一个共用体(<code>union</code>)结构,还使用位域来存储更多的信息。将 64 位的内存数据分开来存储着很多的东西,其中的 33 位才是拿来存储<code>class</code>、<code>meta-class</code>对象的内存地址信息。要通过位运算将<code>isa</code>的值<code>& ISA_MASK</code>掩码,才能得到<code>class</code>、<code>meta-class</code>对象的内存地址。</li></ul>
<div class="jb51code"><pre class="brush:cpp;">// objc.h
struct objc_object {
Class isa;// 在 arm64 架构之前
};
// objc-private.h
struct objc_object {
private:
isa_t isa;// 在 arm64 架构开始
};
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_PACKED_ISA
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
// nonpointer must be the LSB (fixme or get rid of it)
// shiftcls must occupy the same bits that a real class pointer would
// bits + RC_ONE is equivalent to extra_rc + 1
// RC_HALF is the high bit of extra_rc (i.e. half of its range)
// future expansion:
// uintptr_t fast_rr : 1; // no r/r overrides
// uintptr_t lock : 2; // lock for atomic property, @synch
// uintptr_t extraBytes : 1;// allocated with extra bytes
# if __arm64__// 在 __arm64__ 架构下
# define ISA_MASK 0x0000000ffffffff8ULL// 用来取出 Class、Meta-Class 对象的内存地址
# define ISA_MAGIC_MASK0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1;// 0:代表普通的指针,存储着 Class、Meta-Class 对象的内存地址
// 1:代表优化过,使用位域存储更多的信息
uintptr_t has_assoc : 1;// 是否有设置过关联对象,如果没有,释放时会更快
uintptr_t has_cxx_dtor : 1;// 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
uintptr_t shiftcls : 33; // 存储着 Class、Meta-Class 对象的内存地址信息
uintptr_t magic : 6;// 用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1;// 是否有被弱引用指向过,如果没有,释放时会更快
uintptr_t deallocating : 1;// 对象是否正在释放
uintptr_t has_sidetable_rc: 1;// 如果为1,代表引用计数过大无法存储在 isa 中,那么超出的引用计数会存储在一个叫 SideTable 结构体的 RefCountMap(引用计数表)散列表中
uintptr_t extra_rc : 19; // 里面存储的值是对象本身之外的引用计数的数量,retainCount - 1
# define RC_ONE (1ULL<<45)
# define RC_HALF(1ULL<<18)
};
......// 在 __x86_64__ 架构下
};
</pre></div>
<p>如果<code>isa</code>非<code>nonpointer</code>,即 arm64 架构之前的<code>isa</code>指针。由于它只是一个普通的指针,存储着<code>Class</code>、<code>Meta-Class</code>对象的内存地址,所以它本身不能存储引用计数,所以以前对象的引用计数都存储在一个叫<code>SideTable</code>结构体的<code>RefCountMap</code>(引用计数表)散列表中。</p>
<p>如果<code>isa</code>是<code>nonpointer</code>,则它本身可以存储一些引用计数。从以上<code>union isa_t</code>的定义中我们可以得知,<code>isa_t</code>中存储了两个引用计数相关的东西:<code>extra_rc</code>和<code>has_sidetable_rc</code>。</p>
<ul><li>extra_rc:里面存储的值是对象本身之外的引用计数的数量,这 19 位如果不够存储,<code>has_sidetable_rc</code>的值就会变为 1;</li><li>has_sidetable_rc:如果为 1,代表引用计数过大无法存储在<code>isa</code>中,那么超出的引用计数会存储<code>SideTable</code>的<code>RefCountMap</code>中。</li></ul>
<p>所以,如果<code>isa</code>是<code>nonpointer</code>,则对象的引用计数存储在它的<code>isa_t</code>的<code>extra_rc</code>中以及<code>SideTable</code>的<code>RefCountMap</code>中。</p>
<p class="maodian"></p><h3>SideTable</h3>
<div class="jb51code"><pre class="brush:cpp;">// NSObject.mm
struct SideTable {
spinlock_t slock; // 自旋锁
RefcountMap refcnts; // 引用计数表(散列表)
weak_table_t weak_table; // 弱引用表(散列表)
......
}
</pre></div>
<p><code>SideTable</code>存储在<code>SideTables()</code>中,<code>SideTables()</code>本质也是一个散列表,可以通过对象指针来获取它对应的(引用计数表或者弱引用表)在哪一个<code>SideTable</code>中。在非嵌入式系统下,<code>SideTables()</code>中有 64 个<code>SideTable</code>。以下是<code>SideTables()</code>的定义:</p>
<div class="jb51code"><pre class="brush:cpp;">// NSObject.mm
static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;
static StripedMap<SideTable>& SideTables() {
return SideTablesMap.get();
}
</pre></div>
<p>所以,查找对象的引用计数表需要经过两次哈希查找:</p>
<ul><li>① 第一次根据当前对象的内存地址,经过哈希查找从<code>SideTables()</code>中取出它所在的<code>SideTable</code>;</li><li>② 第二次根据当前对象的内存地址,经过哈希查找从<code>SideTable</code>中的<code>refcnts</code>中取出它的引用计数表。</li></ul>
<p>使用多个<code>SideTable</code>+分离锁技术方案是为了保证线程安全的同时兼顾访问效率</p>
<p>以上就是iOS内存管理引用计数示例分析的详细内容,更多关于iOS内存管理引用计数的资料请关注琼殿技术社区其它相关文章!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>iOS开发之MRC(手动内存管理)详解</li><li>iOS内存管理中引用计数的学习</li><li>简述iOS属性中的内存管理参数</li><li>详解关于iOS内存管理的规则思考</li><li>详解iOS应用开发中的ARC内存管理方式</li><li>iOS源码阅读必备知识之Tagged Pointer</li><li>iOS内存管理Tagged Pointer使用原理详解</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]