iOS - AutoreleasePool - 基礎(chǔ)篇

[toc]

參考

AutoreleasePool - base

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 源碼分析>


  1. 先建立一個(gè) autorelease pool;

  2. 對(duì)象從這個(gè) autorelease pool 里面生成;

  3. 對(duì)象生成之后調(diào)用 autorelease 函數(shù), 這個(gè)函數(shù)的作用僅僅是在 autorelease pool 中做個(gè)標(biāo)記, 讓 pool 記得將來(lái) release 一下這個(gè)對(duì)象;

  4. 當(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末略步,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子定页,更是在濱河造成了極大的恐慌趟薄,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件典徊,死亡現(xiàn)場(chǎng)離奇詭異杭煎,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)卒落,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門羡铲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人儡毕,你說(shuō)我怎么就攤上這事也切。” “怎么了腰湾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵雷恃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我费坊,道長(zhǎng)倒槐,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任附井,我火速辦了婚禮讨越,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘永毅。我一直安慰自己谎痢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布卷雕。 她就那樣靜靜地躺著节猿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漫雕。 梳的紋絲不亂的頭發(fā)上滨嘱,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音浸间,去河邊找鬼太雨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛魁蒜,可吹牛的內(nèi)容都是我干的囊扳。 我是一名探鬼主播吩翻,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锥咸!你這毒婦竟也來(lái)了狭瞎?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤搏予,失蹤者是張志新(化名)和其女友劉穎熊锭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雪侥,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碗殷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了速缨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锌妻。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旬牲,靈堂內(nèi)的尸體忽然破棺而出从祝,到底是詐尸還是另有隱情,我是刑警寧澤引谜,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布牍陌,位于F島的核電站,受9級(jí)特大地震影響员咽,放射性物質(zhì)發(fā)生泄漏毒涧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一贝室、第九天 我趴在偏房一處隱蔽的房頂上張望契讲。 院中可真熱鬧,春花似錦滑频、人聲如沸捡偏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)银伟。三九已至,卻和暖如春绘搞,著一層夾襖步出監(jiān)牢的瞬間彤避,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工夯辖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留琉预,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓蒿褂,卻偏偏與公主長(zhǎng)得像圆米,于是被迫代替她去往敵國(guó)和親卒暂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348