2014-12-06 50 views
1

我有一個應用程序在iPhone 4s上使用相機時會發出內存警告。我在使用它之前縮放圖像。瞭解NSAutoreleasePool

+ (UIImage*)simpleImageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize 
{ 

// Create a graphics image context 
UIGraphicsBeginImageContext(newSize); 

// Tell the old image to draw in this new context, with the desired 
// new size 
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)]; 

// Get the new image from the context 
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 

// End the context 
UIGraphicsEndImageContext(); 


// Return the new image. 
return newImage; 
} 

我讀了你應該從這裏http://wiresareobsolete.com/2010/08/uiimagepickercontroller/

所以我修改這樣的代碼使用NSAutoreleasePool:

+ (UIImage*)simpleImageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize 
{ 
//http://wiresareobsolete.com/2010/08/uiimagepickercontroller/ 

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

// Create a graphics image context 
UIGraphicsBeginImageContext(newSize); 

// Tell the old image to draw in this new context, with the desired 
// new size 
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)]; 

// Get the new image from the context 
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 

// End the context 
UIGraphicsEndImageContext(); 

[newImage retain]; 

[pool release]; 

// Return the new image. 
return newImage; 
} 

內存警告就走了。我沒有在單獨的線程中調用它。 這是什麼NSAutoreleasePool在做什麼?我只是不明白這一點。

我可以在本地保留一個對象嗎NSAutoreleasePool

我可以在NSAutoreleasePool已發佈後使用保留的對象嗎?

重要的問題:怎樣的NSAutoreleasePool幫助我的應用程序的內存佔用這個特定的使用情況,以便它不會內存警告?那是你自己創建

+0

現代形式是'@ autoreleasepool' ...一般情況下,當池被耗盡時會發生什麼情況,因爲具有零保留計數的自動釋放對象被釋放,所以當您耗盡池時,您會強制執行dealloc。顯式的'retain'應該會產生警告/錯誤... – stevesliva 2014-12-06 06:42:20

+0

newImage的顯式保留不會導致警告/錯誤。 – 2014-12-06 06:52:32

+0

你沒有使用ARC? – stevesliva 2014-12-06 07:06:33

回答

0

NSAutoreleasePool只是調用釋放的方法在其區域內,其中來自NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];[pool release];的自動釋放對象,

這裏

UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 

newImage被自動釋放對象,[newImage release][pool release];後調用如果不是[newImage retain];newImage將會處理c之後[pool release];

  1. 您可以在本地NSAutoreleasePool中保留一個對象。
  2. 可以在NSAutoreleasePool發佈後使用保留的對象。
  3. 使用NSAutoreleasePool釋放臨時內存。
+0

UIGraphicsGetImageFromCurrentImageContext是否創建了一些標記爲autorelease的大對象?否則我不明白這是如何減少內存使用量的。是不是因爲NSAutoreleasePool被釋放了,所以當我稍後在返回的newImage上調用release時,它立即釋放它的內存而不是減少它的計數以便在稍後時間釋放內存? – 2014-12-06 07:01:12

+0

它應該在系統映像方法中有autorelease對象,我沒有讀過'UIGraphicsGetImageFromCurrentImageContext'源文件,我無法確認它。是的,你對newImage做了什麼,將無助於釋放內存,但有助於保持newImage在autoreleasePool發佈後可以使用。 – simalone 2014-12-06 07:33:20

+0

對我的問題的最後部分有任何想法?爲什麼這樣做會導致iOS停止提供內存警告?AutoreleasePool(ARP)達到某個高水位時會發出內存警告嗎?如果ARP中有對象使用X數量的內存,並且X大於某個值,那麼iOS發送內存警告?我將簡化一個例子。假設最大值是100個單位的內存。如果我有ARP 1,並且它的對象使用了99個單位的內存,並且我添加了另一個對象,那麼iOS會發出警告?如果我有兩個池ARP 1和ARP 2,每個對象使用99個內存單元,那麼沒有警告? – 2014-12-06 15:44:53

4

首先,您應該使用@autoreleasepool塊而不是NSAutoreleasePool對象;後者很不合時宜。前者更好,因爲當你離開塊的範圍時它會自動消耗池,而不需要你自己明確地做。

autorelease池只是記住在其範圍內已經「自動釋放」的事物,並在池被耗盡時釋放它們。 「自動釋放」一個對象就像釋放它,但被推遲;從內存管理的角度來看,它強烈地指出你已經將其「轉移」到池中,以便池現在對該對象有強烈的參考,而你不會。在ARC之前,如果一個方法(其名稱不是以newcopy開頭)需要返回在該函數期間創建的對象,則它幾乎必須被自動釋放。通過將其放在池中,它可以確保該對象將處於活動狀態以便被調用函數接收。在你的代碼中,UIGraphicsGetImageFromCurrentImageContext()可能會返回一個自動釋放對象。

自動釋放池只在池的末尾排空。池中的對象在池的持續時間內保持活動狀態(因爲它們實際上由池所「擁有」)。這意味着如果池持續很長時間並且很多東西都會被自動釋放,那麼很多對象會被阻止被釋放,這可能是不好的。

例如,假設函數的一次運行自動釋放一個對象(即由UIGraphicsGetImageFromCurrentImageContext()返回的對象)。然後,如果你在一個循環中運行你的函數100,000次,那麼100,000個對象會留在內存中,因爲它們會被放到沒有機會流失的同一個池中。但是,如果在循環的每次迭代中放入另一個池級別的池,它將在每次迭代結束時耗盡,從而防止構建對象。

至於你的第二段代碼,在你的simpleImageWithImage:方法中放置一個自動釋放池,確實可以從UIGraphicsGetImageFromCurrentImageContext()獲得autorelease。但是,你有另一個問題,因爲,爲了從simpleImageWithImage:方法本身返回圖像對象,你需要自動釋放它

您寫的方法違反了內存管理規則。你的方法返回一個保留的對象,調用者必須記得釋放它。但是,調用者不知道基於名稱。根據命名約定,唯一可以返回保留實例的方法是名稱以alloc,retain,new,copymutableCopy開頭的那些方法。你的方法不會以任何這些開始,所以它必須返回一個非保留的實例。既然你擁有這個對象(通過保留它),你必須做一個平衡釋放或自動釋放。你不能釋放它,因爲它會導致對象被釋放,因爲沒有其他強引用,所以你只能自動釋放它。

但是,如果你要再次autorelease,你在這裏添加一個自動釋放池,除非在drawInRect:或在UIGraphicsGetImageFromCurrentImageContext()內部自動釋放大量的東西,否則你什麼也沒有做到。