iOS面试知识整理 - 内存管理

196 阅读3分钟

内存管理

内存布局

  • stack: 方法调用
  • heap:通过alloc分配的对象
  • bss:未初始化的全局变量
  • data:已初始化的全局对象
  • text:程序代码 内存结构.png

内存管理方案

iOS怎么对内存进行管理

  1. 小对象- TaggedPointer
  2. 64位 - NONPOINTER_ISA 非指针型isa
  3. 散列表 - 引用计数表、弱引用表

散列表方式

SideTables结构

由多个SideTable组成一个SideTables(分离锁,提高效率),本质是哈希表结构

  1. spinlock_t 自旋锁
  2. RefCountMap 引用计数表
  3. 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的实现

dealloc.png

弱引用

添加weak变量,调用以下方法

  1. objc_initweak()
  2. storeWeak()
  3. weak_register_no_lock()

通过弱应用对象,通过哈希算法的计算寻找,找到它对应的位置。

如果有当前对象的对应弱引用数组,添加到数组;如果没有数组就新增数组,然后把弱引用对应添加到首位

清除weak变量,同时设置指向为nil

delloc() -> ... ->weak_clear_no_lock()

通过弱引用指针,找到弱引用表,将其置为nil

自动释放池

是以栈为结点通过双向链表的形式组合而成

是和线程一一对应的

AutoReleasePoolPage.png

AutoreleasePool的实现原理

在每次runloop将要结束的时候调用AutoReleasePoolPage::Pop()

AutoreleasePool为什么可以嵌套使用?

多层嵌套就是多次插入哨兵对象

什么时候需要手动插入AutoReleasePool

在for循环中alloc图片数据等内存消耗较大的场所中可以

循环引用

  1. 自循环引用
  2. 互相循环引用
  3. 多循环引用

考点

  • 代理
  • 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对象持有等方法