引文:
-
内存布局
-
内存管理方案
-
数据结构
-
ARC&MRC
-
引用计数
-
弱引用
-
自动释放池
-
循环引用
![](https://img.haomeiwen.com/i1361031/14f3d255ce496d44.png)
内存布局 (从低到高分配)
栈(stack
): 方法调用
堆(heap
): 通过alloc分配的对象
bss
: 未初始化的全局变量等
data
: 已初始化的全局变量等
text
: 程序的代码段
内存管理方案
TaggedPointer
: 程序对小数据量优化(防止crash
等)
NONPOINTER_ISA
散列表
怎样实现快速分流?
![](https://img.haomeiwen.com/i1361031/e186dff4c8bd0038.png)
数据结构
Spinlock_t
(自旋锁)
Spinlock_t
是一种"盲等"的锁,(一直循环去询问访问)
适应于轻量的访问
RefcountMap
(引用计数表)
![](https://img.haomeiwen.com/i1361031/362b55994fc56e15.png)
weak_table_t
(弱引用表)
使用对象指针作为key
,通过hash
函数找到到对应的value
![](https://img.haomeiwen.com/i1361031/62c0853dc60a2512.png)
ARC (自动引用计数)
ARC是
LLVM
和Runtime
协作的结果ARC中禁止手动调用
retain/release/retainCount/dealloc
方法ARC中新增
strong
和weak
属性关键字
引用计数管理
alloc
的实现
经过调用链最终会调用c函数
calloc
方法, 并且引用计数不会+1
retain实现
SideTable &table = SideTables()[this];
size_t &refcntStorage = table.refcnts[this];
refcntStorage += SIDE_TABLE_RC_ONE;
retainCount实现
SideTable &table = SideTables()[this];
size_t refcnt_result = 1;
RefcountMap::iterator it = table.refcnts.find(this);
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT
![](https://img.haomeiwen.com/i1361031/35d2afa58d21a5fb.png)
object_dispose()
实现
![](https://img.haomeiwen.com/i1361031/87e921092cf42de3.png)
自动释放池
是以栈为方式的通过双向链表的形式组合而成的
是和线程一一对应的
编译器会将@autoreleasepool{} 改写为:
void *ctx = objc_autoreleasePoolPush();
// 业务逻辑
autoreleasePoolPop(ctx)
AutoreleasePoolPage
![](https://img.haomeiwen.com/i1361031/8402590ad146acdd.png)
AutoreleasePoolPage::pop
根据传入的对象找到入栈的对应位置.
给上次push操作之后添加的对象依次执行release
消息
回退next
指针到正确的位置
在每次runloop
将要结束的时候调用AutoreleasePoolPage::pop()
多层嵌套就是多次插入哨兵对象
在for
循环中alloc
图片数据等占用内存比较大的场景中手动调用autoreleasePool
循环引用
类型:
- 自循环引用
- 相互循环引用
- 多循环引用
一般代理/bolck/NSTimer/大环引用
都有可能引起循环引用, 所有开发者应该尽量避免循环引用,
如果已经产生了循环引用,则需要在合适的时机手动断环
解决方案:
__weak
一般采取这种方式解决循环引用(修饰对象)
OC的特点会对weak
修饰的对象在释放的时候自动置为nil
__block
MRC
:__block
修饰的对象不会增加引用计数,避免了循环引用
ARC
:__block
修饰的对象会产生强引用,无法避免循环引用.需要手动解除
__unsafe_unretained
修饰的对象不会产生循环引用,但是如果对象在某一时刻被释放了会产生悬垂指针
关于NSTimer
产生的循环引用我们可以采用中间对象的形式来解决
网友评论