0%

内存管理

在 Obj-C 中,如何检测内存泄漏?你知道哪些方式?

  • Memory Leaks
  • Alloctions
  • Analyse
  • Debug Memory Graph
  • MLeaksFinder

泄露的内存主要有以下两种:

  • Laek Memory 这种是忘记 Release 操作所泄露的内存。
  • Abandon Memory 这种是循环引用,无法释放掉的内存。

什么是 悬垂指针?什么是 野指针?

  • 悬垂指针
    指针指向的内存已经被释放了,但是指针还存在,这就是一个 悬垂指针 或者说 迷途指针
  • 野指针
    没有进行初始化的指针,其实都是 野指针

retain,copy,assign,weak,_Unsafe_Unretain 关键字的理解

深拷贝 和 浅拷贝 的概念,集合类深拷贝如何实现

自动引用计数应遵循的原则

Dealloc

  1. Dealloc调用流程

    1. 首先调用 _objc_rootDealloc()
    2. 接下来调用 rootDealloc()
    3. 这时候会判断是否可以被释放,判断的依据主要有 5 个,判断是否有以上五种情况
      • NONPointer_ISA
      • weakly_reference
      • has_assoc
      • has_cxx_dtor
      • has_sidetable_rc
    4. 如果有以上五中任意一种,将会调用 object_dispose()方法,做下一步的处理。
    5. 如果没有之前五种情况的任意一种,则可以执行释放操作,C 函数的 free()。 5.执行完毕。
  2. object_dispose() 调用流程。

    1. 直接调用 objc_destructInstance()。
    2. 之后调用 C 函数的 free()。
  3. objc_destructInstance() 调用流程

    1. 先判断 hasCxxDtor,如果有 C++ 的相关内容,要调用 object_cxxDestruct() ,销毁 C++ 相关的内容。
    2. 再判断hasAssocitatedObjects,如果有的话,要调用object_remove_associations(), 销毁关联对象的一系列操作。
    3. 然后调用 clearDeallocating()。
    4. 执行完毕。
  4. clearDeallocating() 调用流程。

    1. 先执行 sideTable_clearDellocating()。
    2. 再执行 weak_clear_no_lock,在这一步骤中,会将指向该对象的弱引用指针置为 nil。
    3. 接下来执行 table.refcnts.eraser(),从引用计数表中擦除该对象的引用计数。
    4. 至此为止,Dealloc 的执行流程结束。

内存管理方案

  • taggedPointer :存储小对象如 NSNumber。深入理解 Tagged Pointer
  • NONPOINTER_ISA(非指针型的 isa):在 64 位架构下,isa 指针是占 64 比特位的,实际上只有 30 多位就
    已经够用了,为了提高利用率,剩余的比特位存储了内存管理的相关数据内容。
  • 散列表:复杂的数据结构,包括了引用计数表和弱引用表
    通过 SideTables()结构来实现的,SideTables()结构下,有很多 SideTable 的数据结构。
    而 sideTable 当中包含了自旋锁,引用计数表,弱引用表。 SideTables()实际上是一个哈希表,通过对象的地址来计算该对象的引用计数在哪个 sideTable 中。

@autoreleasePool 的数据结构?

简单说是双向链表,每张链表头尾相接,有 parent、child 指针 每创建一个池子,会在首部创建一个 哨兵 对象,作为标记
最外层池子的顶端会有一个 next 指针。当链表容量满了,就会在链表的顶端,并指向下一张表。

BAD_ACCESS 在什么情况下出现?

访问了已经被销毁的内存空间,就会报出这个错误。
根本原因是有 悬垂指针 没有被释放。

autoReleasePool 什么时候释放?

App 启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是
_wrapRunLoopWithAutoreleasePoolHandler()。

  • 第一个 Observer 监视的事件是 Entry(即将进入 Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是 -2147483647,优先级最高,保证创 建释放池发生在其他所有回调之前。

  • 第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用 _objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即 将退出 Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

  • 线程结束 Tls_dealloc 释放

  • runloop进入任务,结束任务时 释放

希望对您有所帮助,您的支持将是我莫大的动力!