内存管理

内存布局

  • stack 栈是从高地址向低地址扩展,所以栈是向下增长的,对象和block copy 后都会放在堆上
  • heap 堆区向上增长
  • 散列表方式

    • 自旋锁
      • 自旋锁是“忙等” 的锁
    • 引用计数表
    • 弱应用表

内存管理方案

  • 小对象使用 TaggedPointer
  • 64 位架构下面使用的是 NONPONINT_ISA 内存管理方案,非指针型的 isa,内部存储了一些
  • 散列表结构 (Side Tables() 结构)
    • 自旋锁
    • 引用计数表
    • 弱引用表
    • Side Tables() 结构

为什么不是一个SideTable,而是多个组成了Side Tables

如果所有对象的存储都放在一张大表当中,因为每个对象是在不同的线程中创建的,要操作其中一个对象时,需要将表加锁处理保证数据安全,要等锁释放之后才能操作下一个对象,此时存在效率问题,引入分离锁技术方案

  • 分离锁:把引用计数表分成多张表进行,
  • Side Tables 本质是一张Hash 表

自旋锁

是“忙等”的锁,如果当前锁已被其他线程获取,当前线程会不断探测是否被释放,会第一时间获取,而其他锁比如信号量当它获取不到锁时,会把自己的线程阻塞休眠,等到其他线程释放这个锁时再唤醒这个锁

  • 适用于轻量访问

引用计数表

使用过Hash表来实现,hash查找提高了查找高效率,插入和获取是通过Hash 算法来是实现的,避免了for循环

alloc 实现

经过一系列调用,最终调用了C函数 calloc, 并没有引用计数+1

retain 实现

底层经过了2次Hash表查找

release 实现

和 retain 实现相反

dealloc 实现原理

objc_destructInstance() 实现

clearDeallocating() 实现

MRC

手动引用计数

ARC

  • 编译器在对应位置自动插入retain 和 release 操作,并和runtime协作达到自动引用计数管理内存的效果

  • ARC 中禁止手动调用 retain/release

  • ARC 中新增 weeak、strong 等关键字

弱引用管理

被声明为__weak 的对象指针经过编译后会进过调用以下方法,在最终的weak_register_no_lock() 方法中进行弱引用变量的添加。添加的位置是通过Hash算法查找的

weak 修饰的对象,释放时置为nil 如何实现的

当一个对象被 dealloc 之后,在dealloc内部实现当中会调用弱引用清除的相关函数,在相关函数当中会根据当前对象指针查找弱引用表,把当前对象下对应的弱应用都取出,遍历弱应用指针并置为nil。

自动释放池

是以栈为结点通过双向链表的形式组合而成,和线程一一对应

Autoreleasepool

循环引用

自循环引用

一个对象中有拥有一个 强持有他的 obj,如果给 obj 赋值为原对象就会造成自循环引用

相互循环引用

  • 代理
  • Block
  • NSTimer
  • 大环多循环

多循环引用

解决循环引用的方案

__weak

解决相互循环引用

__block

__unsafe_unretained

一般不建议使用