几乎是默写出来,加上自己理解的博客(iOS面试)
- 内存布局: .text(代码段) .data(常量区:已初始化) .bss(全局静态区:未初始化) heap(堆:alloc对象) stack(栈:方法调用)
内存管理方案:
- TaggedPointer: Tagged Pointer指针本身就可以表示NSNumber了,不需要去堆区再开辟内存
- NONPOINTER_ISA(非指针型isa):isa指针64位,时间记录类地址32或40位就够用了,剩余的这些位中存了一些内存管理相关的数据内容
- 散列表(hash):SideTables(), SideTable(spinlock_t自旋锁 RefcountMap引用计数表 weak_table_t弱引用表)
- 通过一个对象的指针,通过hash算法快速的定位到他属于哪个SideTable表
- 自旋锁(Spinlock_t): 忙等,如引用计数加1
- 引用计数表(RefcountMap):hash表,通过指针可以找到(两次hash查找)对应对象的引用计数
- 弱引用表(weak_table_t): hash表,通过对象指针找到(两次hash查找)对应的弱引用数组(weak_entry_t),数组中的每个元素(weakPtr(N))就是一个弱引用指针
ARC:编译器(LLVM)为我们在相应的地方插入retain,release操作。还需要runtime的协作
- 引用计数管理:相关方法的底层实现
- 弱引用管理:底层实现
- weak自动置为nil:当一个对象被dealloc后,在dealloc的内部实现中,会去调用弱引用清除的相关函数,这个函数的内部实现中,会根据当前对象指针,通过两次hash查找,第一次找到对应的SideTable, 第二次找到对应的弱引用数组。然后遍历这个数组中的所有弱引用指针,分别置为nil
自动释放池
- 是以栈为结点通过双向链表的形式组合而成。是和线程一一对应的(AutoreleasePoolPage)
- 两个哨兵对象之间是一个自动释放池,多层嵌套就是多次插入哨兵对象
- AutoreleasePoolPage::pop 根据传入的哨兵对象找到对应位置,给上传push操作之后添加的对象依次发送release消息,回退next指针到正确位置
- 局部变量array什么时候被释放:在每一次runloop循环中,当他将要结束的时候,会对前一次创建的autoreleasepool进行pop操作,同时会push进来一个新的autoreleasepool。array是在runloop结束autoreleasepool pop的时候释放的
- 使用场景:在for循环中alloc图片数据等内存消耗较大的场景手动插入autoreleasePool,来降低内存的峰值,防止一些内存消耗过大导致的问题
循环引用
|