前一段时间和一同事聊天,聊到一个话题,关于学习曲线的的话题,虽说有很多不一致,但有一点我们一致认同的深度决定高度!为此,我再次看了这本书(Objective-C 高级编程)。
该书从以下三部分出发,自动引用计数、Blocks、Grand Central Dispatch,在源代码的基础上加以解说,深入剖析。 非常建议购买看原版书籍!非常值得常看,因为第一遍可能看不懂,哈哈哈。
在此,我以笔记的形式,边学习,边记录,便于自己坚持。
自动引用计数
什么是自动引用计数
定义:自动引用计数是指内存管理中对引用采取的自动计数的技术。
注意点:在 LLVM 编译器中设置 ARC 为有效状态,就无需再次输入 retain 或者是 release 代码。
好处:这在降低程序奔溃,内存泄露等风险的同时,很大程度上减少了开发程序的工作量。
内存管理 / 引用计数
2.1 引用计数的机制理解
以经典的开灯例子举例,在办公室内至少有一人保持开灯状态,无人时保持关灯状态
最早进入办公室的人 ===> 开灯 ==== > 生成对象
之后进入办公室的人 ===> 需要照明 ==== > 持有对象
下班离开办公室的人 ===> 不需要照明 ==== > 释放对象
最后下班离开办公室的人===> 关灯 ==== > 废弃对象
中间无论多少人,只要遵循进来就需要照明,走就不要照明。这样办公室的照明就可以得到很好的管理;同理使用引用计数功能,对象也能够得到很好的管理。
注意:release方法
当引用计数等于零时会自动使用dealloc方法
。
原则:
- 自己生成的对象,自己持有。
- 非自己生成的对象,自己也能持有。
- 不再需要自己持有的对象时释放。
- 非自己持有的对象无法释放。
** 2.2、alloc,retain,release的实现的理解 **
由于 NSObject 的源码没有公开,此利用 Xcode 的调试器和 iOS 大概追溯其实现过程。
- alloc
+alloc
+allocWithZone:
Class_createInstance
calloc
alloc类方法首先调用 allocWithZone:类方法,然后调用 class_instance 函数,最后通过 calloc 来分配内存块。这样就相当于 alloc 的实现啦。
- retain、release
int __CFDoExternRefOpreation(uintptr_t op, id obj) {
CFBasicHashRef table = 获取对应的是散列表obj(); // 获取对应的是散列表
int count;
switch (op) {
case OPREATION_retainCount:
count = CFBasicHashGetCountOfKey(table, obj);
return count;
case OPREATION_retain:
count = CFBasicHashAddVlaue(table, obj);
return count;
case OPREATION_release:
count = CFBasicHashRemoveValue(table, obj);
// count == 0的时候,调用 dealloc
return 0 == count;
}
}
然后真正的对象实现的方法也许如下:
- (NSUInteger)reatainCount
{
return (NSUInteger) __CFDoExternRefOpreation(OPREATION_retainCount, self);
}
- (id)retain
{
return (id) __CFDoExternRefOpreation(OPREATION_retain, self);
}
- (void)release
{
return __CFDoExternRefOpreation(OPREATION_release, self);
}
苹果的实现大概是采用 散列表(引用计数表)来管理引用计数器。
这样做的好处:
对象用内存筷发分配无需考虑内存块的头部。
引用计数表各记录都有内存块地址,可从各个记录追诉哦到各对象的的内存块。
同时检测内存泄露时,很方便检测各对象的持有者是否存在
以上大致是 retain 、release 的理解,NSObject 在实例化该方法的时候先去调用一个类似 CFDoExternRefOpreation的函数,然后实现相应的方法 CFBasicHashRemoveValue 或 CFBasicHashAddVlaue,通过CFBasicHashRef table (引用计数表)整体的管理它的引用计数,来实现 retain 或 release。这就是大致的实现过程啦。