2003
01 02 03 04 05 06 07 08 09 10 11 12
2006
01 02 03 04 05 06 07 08 09 10 11 12
2007
01 02 03 04 05 06 07 08 09 10 11 12
2008
01 02 03 04 05 06 07 08 09 10 11 12
2009
01 02 03 04 05 06 07 08 09 10 11 12
2010
01 02 03 04 05 06 07 08 09 10 11 12
2011
01 02 03 04 05 06 07 08 09 10 11 12
2017
01 02 03 04 05 06 07 08 09 10 11 12
2018
01 02 03 04 05 06 07 08 09 10 11 12
 
Jun
4
2005

Explanation of NSImage’s leak

先看看底下這段 code,它 allocate/deallocate 了一個 NSData 物件 1000 次。

int i;
for (i = 0; i < 1000; i++) {
    NSData *data= [[NSData alloc] initWithContentsOfFile:@"xxx.jpg"];
    [data release];
}

由於我有正確地作好記憶體管理,每次迴圈結束都 [data release] ,因此這段程式跑起來不會有任何問題。

現在把 NSData 換成 NSImage 試試看:

int i;
for (i = 0; i < 1000; i++) {
    NSImage *img = [[NSImage alloc] initWithContentsOfFile:@"xxx.jpg"];
    [img release];
}

執行程式以後發現記憶體越用越多,用到開始吃 swap ,電腦越來越慢:直到回到 runloop 或是程式結束時,才會一口氣釋放。出了什麼差錯?和 NSData 的範例一樣,img 都有被 release 了啊!難道 release 並沒有真正 release ?還是哪裡有 memory leak?

其實這不是 leak ,只是 NSImage 內部所產生的 autorelease 物件要在回到 runloop 後才會被釋放。改寫成這樣就可以解決問題:

int i;
for (i = 0; i < 1000; i++) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];
    NSImage *img = [[NSImage alloc] initWithContentsOfFile:@"xxx.jpg"];
    [img release];
    [pool drain];
}

如此一來,每次迴圈都可以保證將 NSImage 產生的 autorelease 物件給釋放出來了。

 

Annotations RSS

“剛翻了一下ADC有關NSAutoreleasePool的文件,發現[img release]是否應使用[img autorelease]?
或是兩者作用相同?”

---Andrew. 11/4, 2005

“在最後一個版本的作用都相同啊。為什麼要特地 autorelease ?反正到時候 img 還不是會收 release ,何必多此一舉。”

---yllan. 11/4, 2005

“这个应该可以当作bug处理吧,报告apple了么?”

---Yue Wang. 7/3, 2009

“這不算是 bug,是 tight-loop 時管理記憶體要注意的小地方。 🙂”

---yllan. 7/3, 2009

“我觉得是啊,您看在这个回圈里面,img被我alloc以后又被我release了,况且我在用img的时候用的是alloc而不是一个autorelease的method,因此我完全符合水果文档中的描述,不该有任何记忆体增长啊,所以我个人认为应该理解为img在init的时候,一同产生了一些其他auto release的物件,而这些auto release的附属物件在img被release的时候没有跟着被release(可能NSImage在-dealloc的method中没有call这些物件的dealloc),应此事实上是水果自己的库存在mem leak问题,而且一般来说这个mem leak这个和回圈没多大关系。回圈作用只是到结束的时候清空pool,如果没这个回圈,记忆体在这个过程中依然是不被回收的,直到函数结束。”

---Yue Wang. 7/3, 2009

“謝謝您的說明,不過有幾點我要釐清:

1. 既然這些 memory 「總有一天」(也就是我內文寫的,回到 runloop 後)會被回收,那他們就不能被稱作「leak」。

2. 一般的 getter 都會像這樣 return [[xxx retain] autorelease]; 把物件打入水池,就是為了要延遲釋放。這是目的,並不是 bug。”

---yllan. 7/3, 2009
 
 

Write Concisely