[toc]
參考
https://www.cnblogs.com/XXxiaotaiyang/p/5118737.html
http://www.reibang.com/p/dfec601d84da
http://www.reibang.com/p/b0c19505a5a4
http://www.reibang.com/p/50bdd8438857
https://blog.csdn.net/mr_xiaojie/article/details/52953807
http://www.reibang.com/p/32265cbb2a26
http://blog.sunnyxx.com/2014/10/15/behind-autorelease/
簡(jiǎn)介
AutoreleasePool 是OC中的一種內(nèi)存自動(dòng)回收機(jī)制, 它可以 延遲 加入 AutoreleasePool 中的變量的 release 時(shí)機(jī)盟榴。
在正常情況下, 創(chuàng)建的變量會(huì)在超出其作用域的時(shí)候 release, 但是如果將變量加入 AutoreleasePool , 那么 release 將延遲執(zhí)行处嫌。
autorelease 是 ARC 進(jìn)行引用計(jì)數(shù)管理的一個(gè)機(jī)制雌续。
autorelease 本質(zhì)是把 release 延遲到 autoreleasepool drain 的時(shí)候, 延遲內(nèi)存的釋放。
autorelease 和作用域沒(méi)有任何關(guān)系书妻。
NSAutoreleasePool 是什么?
NSAutoreleasePool 實(shí)際上是個(gè)對(duì)象引用計(jì)數(shù)自動(dòng)處理器, 是一個(gè)繼承于NSObject的類。
OC對(duì)象, 全部繼承自NSObject, 使用引用計(jì)數(shù)的方法來(lái)管理對(duì)象的存活, 眾所周知, 當(dāng)引用計(jì)數(shù)為0時(shí), 對(duì)象就被銷毀了。
操作非常簡(jiǎn)單, 當(dāng)對(duì)象被創(chuàng)建時(shí), 引用計(jì)數(shù)被設(shè)成1躲履〖洌可以給對(duì)象發(fā)送retain消息, 讓對(duì)象對(duì)自己的引用計(jì)數(shù)加1。
而當(dāng)對(duì)象接受到release消息時(shí), 對(duì)象就會(huì)對(duì)自己的引用計(jì)數(shù)進(jìn)行減1, 當(dāng)引用計(jì)數(shù)到了0, 對(duì)象就會(huì)調(diào)用自己的dealloc處理工猜。
當(dāng)對(duì)象被加入到 AutoreleasePool 中, 會(huì)對(duì)其對(duì)象retain一次, 當(dāng) AutoreleasePool 結(jié)束時(shí), 會(huì)對(duì)其所有對(duì)象發(fā)送一次release消息米诉。
AutoreleasePool 可以同時(shí)有多個(gè), 它的組織是個(gè)棧, 總是存在一個(gè)棧頂pool, 也就是當(dāng)前pool;
每創(chuàng)建一個(gè)pool, 就往棧里壓一個(gè), 改變當(dāng)前pool為新建的pool; 每次給pool發(fā)送drain消息, 就彈出棧頂?shù)膒ool, 改當(dāng)前pool為棧里的下一個(gè)pool。
設(shè)計(jì)場(chǎng)景
A retain 了一個(gè)對(duì)象, A有職責(zé) release 它, 但是現(xiàn)在不能release, 因?yàn)锳知道后續(xù)代碼可能想要 retain 它篷帅。
定義一個(gè)函數(shù), 返回對(duì)象A, 方法內(nèi)部 retain 了A, 意味著我們需要 release A, 來(lái)維持A引用計(jì)數(shù)的平衡;
但由于A是函數(shù)返回值, 我們要確保函數(shù)調(diào)用方拿到的對(duì)象A是沒(méi)被回收的, 所以不能在函數(shù)返回前 release史侣。
也就是說(shuō), 我們需要 release 返回值, 但又不能在函數(shù)返回前。
有兩個(gè)選擇, 要么延后 release, 要么函數(shù)調(diào)用方幫我們 release魏身。
這分別對(duì)應(yīng)著解決函數(shù)返回值引用計(jì)數(shù)問(wèn)題的兩種方式:
① autorelease, 即延遲release, 函數(shù)返回前不進(jìn)行 release, 先把返回值暫存在 autoreleasepool 中一段時(shí)間惊橱。
這段時(shí)間內(nèi), 調(diào)用方如果需要, 可以 retain 這個(gè)返回值, 等到 autorelease pool 干(drain)的時(shí)候, 再去 release 這個(gè)對(duì)象, 平衡對(duì)象引用計(jì)數(shù), 適用于除 alloc、copy箭昵、new税朴、mutableCopy 之外的函數(shù)返回值。
② 函數(shù)調(diào)用方負(fù)責(zé) release 函數(shù)返回值, 函數(shù)和函數(shù)調(diào)用方配合維護(hù)引用計(jì)數(shù), 適用于 alloc家制、copy正林、new、mutableCopy 之類的函數(shù)慰丛。
向?qū)ο蟀l(fā)送 autorelease 消息, 會(huì)發(fā)生什么?
將該對(duì)象加入到當(dāng)前 AutoreleasePoolPage 的 棧頂 next 指針 指向的位置卓囚。
autoreleased 對(duì)象的釋放時(shí)機(jī)? ★
加入到 autoreleasepool 中的對(duì)象, 是什么時(shí)候被釋放的?
未手加 AutoreleasePool 的情況下, Autoreleased 對(duì)象是在 當(dāng)前的 runloop 迭代結(jié)束時(shí)釋放的, 而它能夠釋放的原因是, 系統(tǒng)在每個(gè) runloop 迭代中都加入了自動(dòng)釋放池Push和Pop。
autoreleasepool 銷毀時(shí), 會(huì)對(duì) autoreleasepool 里面的所有對(duì)象做一次 release 操作诅病。
autoreleasepool 銷毀時(shí), 在調(diào)用棧中可以發(fā)現(xiàn), 系統(tǒng)調(diào)用了 [NSAutoreleasePool release]
方法, 這個(gè)方法最終通過(guò)調(diào)用 AutoreleasePoolPage::pop(void *)
函數(shù)來(lái)負(fù)責(zé)對(duì) autoreleasepool 中的 autoreleased 對(duì)象執(zhí)行 release 操作哪亿。
雖然 autoreleased 對(duì)象并不都是在代碼塊結(jié)束后就釋放。但是他們有一個(gè)共同特性: 必定是在 @autoreleasepool 被銷毀時(shí)釋放贤笆。
所以要清楚 autoreleased 對(duì)象什么時(shí)候被釋放, 只需要搞清楚 @autoreleasepool 什么時(shí)候被銷毀即可蝇棉。
在 ARC 下, 在線程中的臨時(shí)對(duì)象, 是在當(dāng)前線程的 Runloop 進(jìn)入休眠 或者 退出loop 或者 退出線程時(shí)被執(zhí)行release的。
main() 的 pool
// main() 添加的 autoreleasepool, 在程序退出時(shí)才會(huì)銷毀
// 項(xiàng)目中調(diào)用了 autorelease 的對(duì)象, 并不是被 main() 函數(shù)里添加的 autoreleasepool 管理的
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
手動(dòng)添加的 pool
// MRC 下才能主動(dòng)調(diào)用autorelease
@implementation Person
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
// 手動(dòng)添加的pool, 對(duì)象 release 時(shí)機(jī)就是 `}` 結(jié)束 ★
@autoreleasepool {
Person *person = [[[Person alloc] init] autorelease];
}
NSLog(@"2");
}
@end
輸出
1
-[Person dealloc]
3
runloop 管理的 pool ★
// MRC
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 這個(gè) person 什么時(shí)候調(diào)用 release, 是由 RunLoop 來(lái)控制的
// 它可能是在某次RunLoop循環(huán)中, RunLoop休眠之前調(diào)用了release
// 會(huì)在他所處的那一次 runloop 休眠之前, 被調(diào)用release ★
Person *person = [[[Person alloc] init] autorelease];
NSLog(@"%s", __func__);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%s", __func__);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%s", __func__);
}
@end
輸出:
-[ViewController viewDidLoad]
-[ViewController viewWillAppear:]
-[Person dealloc]
-[ViewController viewDidAppear:]
可見(jiàn), viewDidLoad 和 viewWillAppear 是處在同一次 runloop 中;
分析
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), 1
kCFRunLoopBeforeTimers = (1UL << 1), 2
kCFRunLoopBeforeSources = (1UL << 2), 4
kCFRunLoopBeforeWaiting = (1UL << 5), 32
kCFRunLoopAfterWaiting = (1UL << 6), 64
kCFRunLoopExit = (1UL << 7), 128
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
- (void)viewDidLoad {
[super viewDidLoad];
// 打印出當(dāng)前線程runloop的信息
NSLog(@"%@", NSRunLoop.currentRunLoop);
}
// 輸出可見(jiàn), UITrackingRunLoopMode 和 kCFRunLoopDefaultMode 所注冊(cè)的observer是完全一樣的, 說(shuō)明observer是注冊(cè)到runloop上的, 所有mode共用
// 從輸出內(nèi)容中找到 與 AutoreleasePool 相關(guān)的 CFRunLoopObserver
// activities = 0x01 // kCFRunLoopEntry
<CFRunLoopObserver 0x60000118c140 [0x7fff8062d610]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff48c84b28), context = <CFArray 0x600002ecc390 [0x7fff8062d610]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fa6ca00a048>\n)}}
// activities = 0xa0 // kCFRunLoopBeforeWaiting | kCFRunLoopExit (160 = 32 + 128)
<CFRunLoopObserver 0x60000118c1e0 [0x7fff8062d610]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff48c84b28), context = <CFArray 0x600002ecc390 [0x7fff8062d610]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fa6ca00a048>\n)}}
結(jié)論 ★
iOS 在主線程的 Runloop 中注冊(cè)了2個(gè) AutoreleasePool 相關(guān)的 Observer, 其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler
第1個(gè) observer
監(jiān)聽(tīng)了 kCFRunLoopEntry事件芥永,進(jìn)入時(shí)會(huì)調(diào)用objc_autoreleasePoolPush()
-
第2個(gè) observer
-
監(jiān)聽(tīng)了 kCFRunLoopBeforeWaiting 事件, 休眠之前會(huì)先后調(diào)用
objc_autoreleasePoolPop()
篡殷、objc_autoreleasePoolPush()
所以休眠之前會(huì)將在這之前加入 pool 的對(duì)象, 統(tǒng)一調(diào)用一次 release
監(jiān)聽(tīng)了 kCFRunLoopExit 事件, 退出時(shí)會(huì)調(diào)用
objc_autoreleasePoolPop()
-
這保證了push() 和 pop() 是成對(duì)出現(xiàn)的
<u>這個(gè)結(jié)論對(duì)于其他線程, 應(yīng)該也是適用的。</u>
autoreleasepool 釋放時(shí)機(jī)?
① MRC 下顯式的調(diào)用 drain 方法埋涧。
② 在 runloop 開(kāi)始時(shí), 都會(huì)隱式創(chuàng)建一個(gè) autoreleasepool, 并會(huì)在 runloop 退出時(shí), 把前面創(chuàng)建的 autoreleasepool drain板辽。
釋放順序
主線程中既有系統(tǒng)創(chuàng)建的 @autoreleasepool, 也有開(kāi)發(fā)者手動(dòng)創(chuàng)建的 @autoreleasepool。那么他的釋放順序是怎樣的呢棘催?
因?yàn)?@autoreleasepool 是以棧的形式存儲(chǔ)的, 按照先進(jìn)后出的規(guī)則, 釋放棧中每個(gè)@autoreleasepool劲弦。
系統(tǒng)創(chuàng)建的 pool 是在 Runloop 一開(kāi)始創(chuàng)建的, 所以它必然是棧底的;
手動(dòng)創(chuàng)建的 pool 是在 Runloop 運(yùn)行中創(chuàng)建的, 所以在系統(tǒng)的 pool 上面;
按照棧的規(guī)則, @autoreleasepool 是先釋放自行創(chuàng)建的 pool, 再釋放系統(tǒng)創(chuàng)建的。
與 線程 和 runloop 的關(guān)系:
有沒(méi)有想過(guò)我們直接調(diào)用 autorelease 方法就可以把釋放對(duì)象的任務(wù)交給 Autoreleasepool 對(duì)象, Autoreleasepool 對(duì)象從哪里來(lái)醇坝?
Autoreleasepool 對(duì)象又會(huì)在何時(shí)調(diào)用 [pool drain] 方法邑跪?
每一個(gè)線程, 包括主線程, 都會(huì)擁有一個(gè)專屬的 NSRunLoop 對(duì)象, 并且會(huì)在有需要的時(shí)候自動(dòng)創(chuàng)建。
線程 與 Runloop 是一對(duì)一關(guān)系, 主線程中會(huì)自動(dòng)創(chuàng)建 Runloop, 而子線程需要手動(dòng)獲取。
子線程的 runloop 需要自己手動(dòng)獲取, 如果子線程的 runloop 沒(méi)有任何事件, runloop會(huì)馬上退出画畅。
在每個(gè) loop 開(kāi)始前, 系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè) autoreleasepool , 并在 loop 結(jié)束時(shí) drain 砸琅。
NSAutoreleasePool 中還提到, 每一個(gè)線程都會(huì)維護(hù)自己的 autoreleasepool 堆棧。
換句話說(shuō) autoreleasepool 是與線程緊密相關(guān)的, 每一個(gè) autoreleasepool 只對(duì)應(yīng)一個(gè)線程轴踱。(一對(duì)多???)
系統(tǒng)創(chuàng)建的 pool 和 RunLoop 的關(guān)系
見(jiàn)<>
在主線程執(zhí)行的代碼, 通常是寫(xiě)在諸如事件回調(diào)症脂、Timer回調(diào)內(nèi)的。這些回調(diào)會(huì)被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著, 所以不會(huì)出現(xiàn)內(nèi)存泄漏, 開(kāi)發(fā)者也不必顯式創(chuàng)建 Pool 了淫僻。
也就是說(shuō) AutoreleasePool 創(chuàng)建是在一個(gè)RunLoop事件開(kāi)始之前(push), AutoreleasePool釋放是在一個(gè)RunLoop事件即將結(jié)束之前(pop)摊腋。
AutoreleasePool 里的 Autorelease 對(duì)象的加入是在RunLoop事件中,
AutoreleasePool 里的 Autorelease 對(duì)象的釋放是在 AutoreleasePool 釋放時(shí)。
手動(dòng)創(chuàng)建的 @autoreleasepool
@autoreleasepool { // 這個(gè) `{` 開(kāi)始, 創(chuàng)建自動(dòng)釋放池, 內(nèi)部的對(duì)象自動(dòng)加入autorelease
} // 這個(gè) `}` 開(kāi)始, 自動(dòng)釋放池被銷毀嘁傀。
子線程上的 autoreleasepool? ★
子線程默認(rèn)不會(huì)開(kāi)啟 Runloop, 對(duì)象調(diào)用 autorelease 如何處理? 不手動(dòng)處理會(huì)內(nèi)存泄漏嗎?
① 若當(dāng)前線程已經(jīng)創(chuàng)建了 Pool , Autoreleased 對(duì)象就會(huì)交給 pool 去管理兴蒸。
② 若當(dāng)前線程沒(méi)有pool, 代碼調(diào)用順序?yàn)? autorelease -> autoreleaseFast -> autoreleaseNoPage
。
在 autoreleaseNoPage
方法中, 會(huì)創(chuàng)建一個(gè)hotPage
, 然后調(diào)用 page->add(obj)
將對(duì)象添加到 AutoreleasePoolPage
的棧中细办。
也就是說(shuō)即使當(dāng)前線程沒(méi)有pool (沒(méi)有開(kāi)啟runloop), 對(duì)象調(diào)用autorelease時(shí), 也會(huì) new 一個(gè) AutoreleasepoolPage 出來(lái)管理autorelease對(duì)象, 不用擔(dān)心內(nèi)存泄漏橙凳。
<u>也就是說(shuō), 不一定要runloop開(kāi)啟, 才能創(chuàng)建pool</u>
這個(gè)是 OS X 10.9+ 和 iOS 7+ 才加入的特性。并且蘋(píng)果沒(méi)有對(duì)應(yīng)的官方文檔闡述此事, 但是你可以通過(guò)源碼了解笑撞。
子線程的 AutoreleasepoolPage 和主線程的 page 有關(guān)聯(lián)嗎?
沒(méi)有, 釋放池和 線程是一一對(duì)應(yīng)的
實(shí)現(xiàn)原理 ★
見(jiàn)<autoreleasepool 源碼分析>
先建立一個(gè) autorelease pool;
對(duì)象從這個(gè) autorelease pool 里面生成;
對(duì)象生成之后調(diào)用 autorelease 函數(shù), 這個(gè)函數(shù)的作用僅僅是在 autorelease pool 中做個(gè)標(biāo)記, 讓 pool 記得將來(lái) release 一下這個(gè)對(duì)象;
當(dāng) pool 要把池中的對(duì)象 release 時(shí), pool本身也需要rerlease, 此時(shí) pool 會(huì)把每一個(gè)標(biāo)記為 autorelease 的對(duì)象 release 一次;
但, 如果某個(gè)對(duì)象此時(shí) retain count 大于1, 這個(gè)對(duì)象還是沒(méi)有被銷毀;
被標(biāo)為autorelease的對(duì)象, 并不是等程序結(jié)束時(shí)才release, 如:
在 viewDidLoad 中創(chuàng)建一個(gè) autorelease 對(duì)象, 在 viewDidLoad 走完, 一次消息循環(huán)完畢, 這個(gè) autorelease pool 中的對(duì)象就會(huì)被 release;
button 的點(diǎn)擊事件, 點(diǎn)擊代碼執(zhí)行完會(huì)release一下autorelease類型的變量;
上面這個(gè)例子應(yīng)該這樣寫(xiě):
ClassName *myName = [[[ClassName alloc] init] autorelease]; // 標(biāo)記為autorelease
[classA setName:myName]; // retain count == 2 (如果myName的屬性是retain的話)
[myName release]; // retain count == 1, 注意, 在ClassA的dealloc中不能release name, 否則release pool時(shí)會(huì)release這個(gè)retain count為0的對(duì)象, 這是不對(duì)的岛啸。
記住一點(diǎn):
如果這個(gè)對(duì)象是你alloc或者new出來(lái)的, 你就需要調(diào)用release。如果使用autorelease, 那么僅在發(fā)生過(guò)retain的時(shí)候release一次(讓retain count始終為1)茴肥。
Autoreleasepool 對(duì)象從哪里來(lái)?
對(duì)于每一個(gè)Runloop運(yùn)行循環(huán), 系統(tǒng)會(huì)隱式創(chuàng)建一個(gè) Autoreleasepool 對(duì)象,
+ (instancetype)student; 中執(zhí)行autorelease的操作, 就會(huì)將student對(duì)象添加到這個(gè)系統(tǒng)隱式創(chuàng)建的Autoreleasepool 中
Autoreleasepool 對(duì)象又會(huì)在何時(shí)調(diào)用 [pool drain] 方法?
當(dāng)Runloop執(zhí)行完一系列動(dòng)作沒(méi)有更多事情要它做時(shí), 它會(huì)進(jìn)入休眠狀態(tài), 避免一直占用大量系統(tǒng)資源, 或者Runloop要退出時(shí), 會(huì)觸發(fā)執(zhí)行_objc_autoreleasePoolPop()
方法, 相當(dāng)于讓 Autoreleasepool 對(duì)象執(zhí)行一次 drain 方法,
Autoreleasepool 對(duì)象會(huì)對(duì)自動(dòng)釋放池中所有的對(duì)象依次執(zhí)行依次release操作
為什么對(duì)象在被釋放前, 打印出來(lái)的retainCount為1而不為0?
當(dāng)對(duì)象最后一次執(zhí)行release時(shí), 系統(tǒng)知道馬上就要回收內(nèi)存了, 就沒(méi)有必要再將retainCount減1了, 因?yàn)椴还軠p不減1, 該對(duì)象都肯定會(huì)被回收, 而對(duì)象被回收后, 它的所有的內(nèi)存區(qū)域, 包括retainCount值也變得沒(méi)有意義坚踩。不將這個(gè)值從1變成0, 可以減少一次內(nèi)存的操作, 加速對(duì)象的回收。
每個(gè)線程只有一個(gè) autoreleasepool 嗎?
可以手動(dòng)添加 局部釋放池
一個(gè)線程有幾個(gè) autoreleasepool棧?
autoreleasepool棧 的說(shuō)法應(yīng)該是 autoreleasepoolPage, 一個(gè)autoreleasepool 可以有多個(gè)page
enumerateObjectsUsingBlock
使用容器的block版本的枚舉器時(shí), 內(nèi)部會(huì)自動(dòng)添加一個(gè)AutoreleasePool:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 這里被一個(gè)局部 @autoreleasepool 包圍著
}];
當(dāng)然, 在普通for循環(huán)和for in循環(huán)中沒(méi)有, 所以, 還是新版的block版本枚舉器更加方便瓤狐。for循環(huán)中遍歷產(chǎn)生大量autorelease變量時(shí), 就需要手加局部AutoreleasePool咯瞬铸。
當(dāng)一個(gè) runloop 在不停的循環(huán)工作, 那么runloop每一次循環(huán)必定會(huì)經(jīng)過(guò)BeforeWaiting(準(zhǔn)備進(jìn)入休眠):而去BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊池并創(chuàng)建新池, 那么這兩個(gè)方法來(lái)銷毀要釋放的對(duì)象, 所以我們根本不需要擔(dān)心Autorelease的內(nèi)存管理問(wèn)題, 這就是ARC背后的“高人”。
當(dāng)對(duì)象調(diào)用 autorelease 方法時(shí), 會(huì)將對(duì)象加入 AutoreleasePoolPage 的棧中
調(diào)用 AutoreleasePoolPage::pop 方法會(huì)向棧中的對(duì)象發(fā)送 release 消息
什么對(duì)象會(huì)入池? ★
被加入 autoreleasepool 的對(duì)象, 怎樣進(jìn)行內(nèi)存管理?
對(duì)象被加入到離該對(duì)象最近的 autoreleasepool 中, 只有當(dāng)這個(gè) pool drain 時(shí), pool 中的 autoreleased 對(duì)象才會(huì)被 release ( 當(dāng)retainCount = 0 時(shí)對(duì)象才被釋放)础锐。
未被加入 autoreleasepool 的對(duì)象, 怎樣進(jìn)行內(nèi)存管理?
未被加入 autoreleasepool 的對(duì)象, 都是由系統(tǒng)管理, 根據(jù)引用計(jì)數(shù)來(lái)控制釋放的時(shí)機(jī), 在適當(dāng)?shù)奈恢?release嗓节。
什么樣的對(duì)象會(huì)交給自動(dòng)釋放池管理? ★
MRC下, 對(duì)象調(diào)用
autorelease
方法;ARC下, 給對(duì)象添加
__autoreleasing
修飾符;非自己創(chuàng)建的對(duì)象: 一般是系統(tǒng)提供的類方法 / 工廠方法, 創(chuàng)建的對(duì)象如
[NSArray array];
。函數(shù)返回值: 對(duì)象作為方法的返回值時(shí), 也會(huì)被附加上
__autoreleasing
修飾符; (其實(shí)這就是非自己創(chuàng)建的對(duì)象)
什么是自己創(chuàng)建的對(duì)象?
自己創(chuàng)建的對(duì)象: 使用 alloc皆警、new拦宣、copy、mutablecopy 及其駝峰變形 allocObject信姓、newObject鸵隧、copyObject、mutablecopyObject 的方法創(chuàng)建的對(duì)象;
自己創(chuàng)建的對(duì)象, 遵循
"誰(shuí)創(chuàng)建, 誰(shuí)釋放, 誰(shuí)引用, 誰(shuí)管理"
的內(nèi)存管理原則, 在用完之后直接release
拿NSArray來(lái)舉例, 什么情況會(huì)入池?
使用
[[NSArray alloc] init];
創(chuàng)建的對(duì)象, 不會(huì)入池意推。使用
[[[NSArray alloc] init] autorelease];
(MRC下)調(diào)用了autorelease, 入池豆瘫。使用
[NSArray array];
即調(diào)用 Foundation 的類方法/工廠方法創(chuàng)建出來(lái)的對(duì)象, 入池。
id 的指針或?qū)ο蟮闹羔? 在沒(méi)有顯式指定修飾符時(shí), 會(huì)被默認(rèn)加上 __autoreleasing 修飾符, 注冊(cè)到 autoreleasepool 中左痢。
__weak對(duì)象
__weak 修飾的對(duì)象, 為了保證在引用時(shí)不被廢棄, 會(huì)注冊(cè)到 autoreleasepool 中靡羡。這是蘋(píng)果之前的實(shí)現(xiàn)
現(xiàn)在 ARC 不會(huì) autorelease 弱引用對(duì)象, 而是直接 release
__weak 保證了弱指針使用期間, 狀態(tài)的一致性。LLVM8的最新實(shí)現(xiàn)是, 不推遲release到autoreleasepool, 而是直接release俊性。
https://stackoverflow.com/questions/40993809/why-weak-object-will-be-added-to-autorelease-pool
In conclusion, this design of __weak ensure that during the usage of weak pointer, its state is consistent. The new implmenetation of __weak of Apple LLVM version 8.0.0 (clang-800.0.42.1) do not postponed the release to autoreleasepool, but use objc_release directly.
方法里有局部對(duì)象, 出了方法作用域會(huì)立刻釋放嗎?
答: 會(huì)立刻釋放
// ARC
@implementation Person
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%s", __func__);
}
@end
輸出:
-[ViewController viewDidLoad]
-[Person dealloc]
-[ViewController viewWillAppear:]
可見(jiàn) viewDidLoad 執(zhí)行完, 局部變量就 dealloc 了;
猜測(cè) ARC 是在局部變量作用域結(jié)束之前, 插入了一句 release