IOS内存管理--手动引用计数实现
2015-08-05 21:53
344 查看
我的上一篇博客从手动引用计数、ARC两个方面介绍了IOS的内存管理机制,接下来简单介绍手动引用计数管理方式的部分实现,源码参照的是objc4-646,下载链接:http://opensource.apple.com/source/objc4/。
在手动引用计数管理方式中,最重要的是如何管理对象的引用计数,那么我们就从引用计数及其管理方式的实现入手。首先我们建立一个简单的命令行程序,在Build Settings 中将ARC设置为NO:
然后我们在main函数中写下如下简单的代码:
上面的代码段中涉及了我们手动引用计数管理的部分基本方法,那么我想知道上面的方法都调用了系统的哪些方法,该怎么查看?Xcode给出了我们很简单的方式,如下图所示:
我们看到Callees列出了我们上面代码段所调用的方法,Callers列出我们的方法被哪些方法调用,大家可以自行查看,据我观察调用顺序跟你所写的代码顺序没有明确的对应关系。
首先我们查看 +[NSObject alloc],通过查看源代码,方法的调用顺序是:
+(id)alloc;
id _objc_rootAlloc(Class cls);
id callAlloc(Class cls, bool checkNil, bool allocWithZone=false);
id class_createInstance(Class cls, size_t extraByte,nil);
calloc;
对象初始化,大都和申请内存相关,具体不再多说。
然后我们来看retain都涉及到哪些方法调用:
id objc_retain(id obj);
- (id)retain;
inline id objc_object::rootRetain();
id objc_object::sidetable_retain();
然后我们可以看看最后一个函数的具体实现:
首先我们看一下SideTable中是怎么存储对象的引用计数:RefcountMap refcnts;然后RefcountMap的定义如下:
从上面我们可以看到,对象的引用计数实现是类似的散列表,保存着引用计数值和对应拥有者对象的地址,这种实现方式在检测内存泄露的时候可以直接定位到具体的对象。
只要明白了上面的实现原理,其他release/retainCount的实现就很容易理解了,接下来我们看看release的方法调用简单罗列如下:
release
rootRelease
sidetable_release
同样我们还是来看最后一个函数的实现:
从代码中我们可以看出,首先要判断该引用计数是否为引用计数表中的最后一个,如果是则表示release后需要执行dealloc清除对象所占用的内存。
那么接下来我们看看retainCount的具体实现,我们从最后一个函数:
从这里我们看出,在计算一个对象的引用计数的时候,会遍历该对象引用计数表,将所有的引用计数求和。
那么有关引用计数管理操作的简单实现就跟大家分享到这里,如有错误还请大家指正。
在手动引用计数管理方式中,最重要的是如何管理对象的引用计数,那么我们就从引用计数及其管理方式的实现入手。首先我们建立一个简单的命令行程序,在Build Settings 中将ARC设置为NO:
然后我们在main函数中写下如下简单的代码:
//初始化对象 id object = [[[NSObject alloc]init] autorelease]; // 引用计数加 1 [object retain]; // 打印引用计数为2 NSLog(@"object1 %lu",(unsigned long)[object retainCount]); // 引用计数减 1,为1 [object release]; // 引用计数减 1 ,为0,然后释放该对象 [object release];
上面的代码段中涉及了我们手动引用计数管理的部分基本方法,那么我想知道上面的方法都调用了系统的哪些方法,该怎么查看?Xcode给出了我们很简单的方式,如下图所示:
我们看到Callees列出了我们上面代码段所调用的方法,Callers列出我们的方法被哪些方法调用,大家可以自行查看,据我观察调用顺序跟你所写的代码顺序没有明确的对应关系。
首先我们查看 +[NSObject alloc],通过查看源代码,方法的调用顺序是:
+(id)alloc;
id _objc_rootAlloc(Class cls);
id callAlloc(Class cls, bool checkNil, bool allocWithZone=false);
id class_createInstance(Class cls, size_t extraByte,nil);
calloc;
对象初始化,大都和申请内存相关,具体不再多说。
然后我们来看retain都涉及到哪些方法调用:
id objc_retain(id obj);
- (id)retain;
inline id objc_object::rootRetain();
id objc_object::sidetable_retain();
然后我们可以看看最后一个函数的具体实现:
id objc_object::sidetable_retain() { #if SUPPORT_NONPOINTER_ISA assert(!isa.indexed); #endif // 获得该对象的table对象 SideTable *table = SideTable::tableForPointer(this); // 类似上锁的操作 if (spinlock_trylock(&table->slock)) { // 在table表中的refcnts属性中获取 引用计数的引用 size_t& refcntStorage = table->refcnts[this]; // 如果引用计数存在且计数没有越界 if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) { //#define SIDE_TABLE_RC_ONE (1UL<<2) // MSB-ward of deallocating bit refcntStorage += SIDE_TABLE_RC_ONE; } spinlock_unlock(&table->slock); return (id)this; } return sidetable_retain_slow(table); }
首先我们看一下SideTable中是怎么存储对象的引用计数:RefcountMap refcnts;然后RefcountMap的定义如下:
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
从上面我们可以看到,对象的引用计数实现是类似的散列表,保存着引用计数值和对应拥有者对象的地址,这种实现方式在检测内存泄露的时候可以直接定位到具体的对象。
只要明白了上面的实现原理,其他release/retainCount的实现就很容易理解了,接下来我们看看release的方法调用简单罗列如下:
release
rootRelease
sidetable_release
同样我们还是来看最后一个函数的实现:
bool objc_object::sidetable_release(bool performDealloc) { #if SUPPORT_NONPOINTER_ISA assert(!isa.indexed); #endif SideTable *table = SideTable::tableForPointer(this); bool do_dealloc = false; if (spinlock_trylock(&table->slock)) { RefcountMap::iterator it = table->refcnts.find(this); if (it == table->refcnts.end()) { do_dealloc = true; table->refcnts[this] = SIDE_TABLE_DEALLOCATING; } else if (it->second < SIDE_TABLE_DEALLOCATING) { // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it. do_dealloc = true; it->second |= SIDE_TABLE_DEALLOCATING; } else if (! (it->second & SIDE_TABLE_RC_PINNED)) { it->second -= SIDE_TABLE_RC_ONE; } spinlock_unlock(&table->slock); if (do_dealloc && performDealloc) { ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc); } return do_dealloc; } return sidetable_release_slow(table, performDealloc); }
从代码中我们可以看出,首先要判断该引用计数是否为引用计数表中的最后一个,如果是则表示release后需要执行dealloc清除对象所占用的内存。
那么接下来我们看看retainCount的具体实现,我们从最后一个函数:
uintptr_t objc_object::sidetable_retainCount() { SideTable *table = SideTable::tableForPointer(this); size_t refcnt_result = 1; spinlock_lock(&table->slock); RefcountMap::iterator it = table->refcnts.find(this); if (it != table->refcnts.end()) { // this is valid for SIDE_TABLE_RC_PINNED too refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT; } spinlock_unlock(&table->slock); return refcnt_result; }
从这里我们看出,在计算一个对象的引用计数的时候,会遍历该对象引用计数表,将所有的引用计数求和。
那么有关引用计数管理操作的简单实现就跟大家分享到这里,如有错误还请大家指正。
相关文章推荐
- 查询linux发行版本号方法总结
- 查询linux发行版本号方法总结
- 谁写了Linux
- 添加错误debug信息
- ARC 使用规则
- Sniffer技术文档
- Infinispan 7.0.1.Final 发布,数据网格平台
- 如何使用编译生成的release搭建nfs boot环境
- @property (nonatomic,retain)中的nonatom和retain是什么意思
- centos和redhat启用epel软件库
- spring源码下载
- cocos2dx的内存管理机制---lamp兄弟连
- Linux命令精选-显示发行版
- Linux Tomcat 6.0安装配置实践总结
- Linux 不能挂载NTFS的U盘解决方法
- ios property探秘
- NSString为啥要使用Copy属性
- VC下Debug和Release区别
- Debug和Release区别
- Debug与Release版本的区别