内存管理
内存布局
- stack: 方法调用
- heap:通过alloc分配的对象
- bss:未初始化的全局变量
- data:已初始化的全局对象
- text:程序代码
内存管理方案
iOS怎么对内存进行管理
- 小对象- TaggedPointer
- 64位 - NONPOINTER_ISA 非指针型isa
- 散列表 - 引用计数表、弱引用表
散列表方式
SideTables结构
由多个SideTable组成一个SideTables(分离锁,提高效率),本质是哈希表结构
- spinlock_t 自旋锁
- RefCountMap 引用计数表
- weak_table_t 弱引用表
如何快速分流?
哈希查找,提高查找效率
数据结构
1 Spinlock_t 自旋锁
- Spinlock_t是“忙等“的锁
- 适用于轻量访问
2 RefcountMap 引用计数表
哈希表,是个map,是哈希查找
ptr(内存地址)通过DisguisedPtr(obj_object)->size_t引用计数(64位)
3 weak_table_t 弱引用表
哈希表
key(对象指针)通过Hash函数查找value-弱引用表(weak_entry_t)
ARC&MRC
MRC
手动引用计数
- alloc
- retain
- release
- retainCount
- Autorelease
- dealloc
ARC
自动引用计数
- ARC是LLVM(自动插入retain和release)和Runtime协作的结果
- ARC中禁止手动调用retain/release/retainCount/delloc(super)
- ARC中新增了strong、weak关键字
引用计数
alloc实现
经过一系列调用,最终调用了C函数calloc
此时并没有设置引用计数为1
retain实现
两次Hash查找,size_t 无符号long值
SideTable& table = SideTables()[this];
size_t& refcntStorage = table.refcnts[this];
refcntStorage += SIDE_TABLE_RC_ONE;
release实现
SideTable& table = SideTables()[this];
RefcountMap::iterator it = table.refcnts.find[this];
it->second -= 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;
delloc的实现
弱引用
添加weak变量,调用以下方法
- objc_initweak()
- storeWeak()
- weak_register_no_lock()
通过弱应用对象,通过哈希算法的计算寻找,找到它对应的位置。
如果有当前对象的对应弱引用数组,添加到数组;如果没有数组就新增数组,然后把弱引用对应添加到首位
清除weak变量,同时设置指向为nil
delloc() -> ... ->weak_clear_no_lock()
通过弱引用指针,找到弱引用表,将其置为nil
自动释放池
是以栈为结点通过双向链表的形式组合而成
是和线程一一对应的
AutoreleasePool的实现原理
在每次runloop将要结束的时候调用AutoReleasePoolPage::Pop()
AutoreleasePool为什么可以嵌套使用?
多层嵌套就是多次插入哨兵对象
什么时候需要手动插入AutoReleasePool
在for循环中alloc图片数据等内存消耗较大的场所中可以
循环引用
- 自循环引用
- 互相循环引用
- 多循环引用
考点
- 代理
- Block
- NSTimer
- 大环引用
如何破除循环引用
- 避免循环引用
- 在合适的时候手动断开
具体方案
- __weak
- __block :arc中会会被强引用,需要手动断开
- __unsafe_unretained:不会增加引用计数,释放会出现野指针
面试总结
什么是ARC?
ARC是系统LLVM(自动添加retain和release)和runtime协作出来的,是采用自动引用计数的一种内存管理方案
为什么weak指针指向的对象在废弃之后会被自动置为nil?
当对象被废弃后,dealloc方法内部调用weak_clear_no_lock方法,弱引用指针通过hash算法,找到对应的弱应用表的size_t,将弱引用数组里面的弱引用指针都置为nil
苹果是如何实现AutoReleasePool?
AutoReleasePool是以栈为节点的双链表结构
什么是循环引用?遇到过那些?怎么解决?
简单说就是互相持有对方,而且是强持有,导致引用计数无法为0,始终无法释放;
循环引用有三种,自循环引用,互相循环引用以及多循环引用;
循环引用会导致内存无法回收,有block,delegate,nstimer还有大环引用
__weak, NSTimer 中间weak对象持有等方法