小碼哥iOS學習筆記第二十六天: @autoreleasepool

1、@autoreleasepool編譯後的代碼

  • 定義Person類繼承自NSObject, 在main函數中代碼以下

  • 經過終端cdmain.m所在文件夾, 並執行下面的命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
複製代碼
  • 將生成的main.cpp文件拖到項目中並打開, 能夠找到main.m文件在底層的代碼

  • 整理後, 就是下面的代碼

  • 搜索__AtAutoreleasePool結構體

  • 整理後以下

  • 咱們知道, 一個自動釋放池會自動銷燬, 因此main函數中的代碼能夠模擬爲下圖所示

  • 因此, 想要了解@autoreleasepool, 能夠從objc_autoreleasePoolPush函數和objc_autoreleasePoolPop函數入手

2、objc_autoreleasePoolPush 和 objc_autoreleasePoolPop

  • objc_autoreleasePoolPush函數的底層實現

  • objc_autoreleasePoolPop函數的底層實現

  • 能夠看到objc_autoreleasePoolPushobjc_autoreleasePoolPop, 都是用了AutoreleasePoolPage
  • 下圖中展現的是AutoreleasePoolPage中的主要成員變量

自動釋放池的主要底層數據結構是:__AtAutoreleasePool、AutoreleasePoolPage
調用了autorelease的對象最終都是經過AutoreleasePoolPage對象來管理的ios

3、AutoreleasePoolPage的結構

  • 每一個AutoreleasePoolPage對象佔用4096字節內存,除了用來存放它內部的成員變量,剩下的空間用來存放autorelease對象的地址

  • AutoreleasePoolPage中除了一開始的56個字節用來存儲成員變量, 其餘的全部內存空間都是用來存儲被autorelease對象的地址
  • 其中beginend兩個函數的實現以下

begin = AutoreleasePoolPage地址 + AutoreleasePoolPage的大小
end = AutoreleasePoolPage地址 + SIZE(4096)緩存

  • 當一個AutoreleasePoolPage不夠存儲autorelease對象地址時, 就會在建立一個AutoreleasePoolPage
  • 全部的AutoreleasePoolPage對象經過雙向鏈表的形式鏈接在一塊兒

4、AutoreleasePoolPage的push和pop

  • 調用push方法會將一個POOL_BOUNDARY入棧,而且返回其存放的內存地址bash

  • 調用pop方法時傳入一個POOL_BOUNDARY的內存地址,會從最後一個入棧的對象開始發送release消息,直到遇到這個POOL_BOUNDARY數據結構

  • id *next指向了下一個能存放autorelease對象地址的區域iphone

  • 能夠經過如下私有函數來查看自動釋放池的狀況 extern void _objc_autoreleasePoolPrint(void);函數

  • 使用_objc_autoreleasePoolPrint函數, 查看一個空的autoreleasepooloop

  • 能夠看到, 此時自動緩衝池中並無任何的autorelease對象

  • 添加一個autorelease對象

  • 能夠看到page中有兩個緩存, 其中一個POOL就是POOL_BOUNDARY, 第二個就是加入的Person對象

  • 再添加一個autorelease對象

  • 此時page中存儲三個內容, 一個POOL_BOUNDARY, 兩個Person對象

  • 嵌套一層@autoreleasepool, 代碼以下

  • 此時能夠看到page中存儲了兩個POOL_BOUNDARY

  • 再次嵌套一層@autoreleasepool, 代碼以下

  • 此時能夠看到page中存儲了三個POOL_BOUNDARY

  • @autoreleasepool存放1000Personautorelease對象

  • 能夠看到, 一個page存儲空間用滿了, 會再次建立一個page

5、Runloop和Autorelease

  • iOS在主線程的Runloop中註冊了2個Observer
  • 第1個Observer監聽了kCFRunLoopEntry事件,會調用objc_autoreleasePoolPush()
  • 第2個Observer
  • 監聽了kCFRunLoopBeforeWaiting事件,會調用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
  • 監聽了kCFRunLoopBeforeExit事件,會調用objc_autoreleasePoolPop()
相關文章
相關標籤/搜索