内存管理相关



引文:
  • 多线程相关
  • OC 语言相关
  • 内存管理相关
  • UI视图相关
  • RunLoop相关


  1. 内存布局
  2. 内存管理方案
  3. 数据结构
  4. ARC&MRC
  5. 引用计数
  6. 弱引用
  7. 自动释放池
  8. 循环引用
image.png
内存布局 (从低到高分配)

栈(stack): 方法调用

堆(heap): 通过alloc分配的对象

bss: 未初始化的全局变量等

data: 已初始化的全局变量等

text: 程序的代码段

内存管理方案

TaggedPointer: 程序对小数据量优化(防止crash等)

NONPOINTER_ISA

散列表

怎样实现快速分流?

image.png
数据结构

Spinlock_t (自旋锁)

Spinlock_t 是一种"盲等"的锁,(一直循环去询问访问)

适应于轻量的访问

RefcountMap (引用计数表)

image.png

weak_table_t (弱引用表)

使用对象指针作为key,通过hash函数找到到对应的value

image.png
ARC (自动引用计数)

ARC是LLVMRuntime协作的结果

ARC中禁止手动调用retain/release/retainCount/dealloc方法

ARC中新增strongweak属性关键字

引用计数管理

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
image.png

object_dispose()实现

image.png
自动释放池

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

编译器会将@autoreleasepool{} 改写为:

void *ctx = objc_autoreleasePoolPush();
  // 业务逻辑
autoreleasePoolPop(ctx)
AutoreleasePoolPage
image.png

AutoreleasePoolPage::pop

根据传入的对象找到入栈的对应位置.
给上次push操作之后添加的对象依次执行release消息
回退next指针到正确的位置
在每次runloop将要结束的时候调用AutoreleasePoolPage::pop()
多层嵌套就是多次插入哨兵对象
for循环中alloc图片数据等占用内存比较大的场景中手动调用autoreleasePool

循环引用

类型:

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

一般代理/bolck/NSTimer/大环引用 都有可能引起循环引用, 所有开发者应该尽量避免循环引用,
如果已经产生了循环引用,则需要在合适的时机手动断环

解决方案:

__weak

一般采取这种方式解决循环引用(修饰对象)
OC的特点会对weak 修饰的对象在释放的时候自动置为nil

__block

MRC: __block修饰的对象不会增加引用计数,避免了循环引用
ARC:__block修饰的对象会产生强引用,无法避免循环引用.需要手动解除

__unsafe_unretained

修饰的对象不会产生循环引用,但是如果对象在某一时刻被释放了会产生悬垂指针

关于NSTimer 产生的循环引用我们可以采用中间对象的形式来解决

你可能感兴趣的:(内存管理相关)