2010-05-28 51 views
3

出於某種原因,下面代碼中的保留/釋放行爲讓我完全困惑。UIImage imageNamed沒有正確地自動解除

selectedImage = [UIImage imageNamed:@"icon_72.png"]; 
[selectedImage release]; 

應該突破,但。爲什麼?我認爲imageNamed autoreleased本身,這意味着這裏釋放是多餘的,應該在autorelease發生時中斷。

這裏是從h和.m文件有關selectedImage片段:

@property (nonatomic, readonly) UIImage *selectedImage; 
@synthesize delegate, selectedImage, spacerBottom, currentIndex; 

其他注意事項,這確實突破:

selectedImage = [UIImage imageNamed:@"icon_72.png"]; 
[selectedImage release]; 
[selectedImage release]; 
//objc[55541]: FREED(id): message release sent to freed object=0x59245b0 
//Program received signal: 「EXC_BAD_INSTRUCTION」. 

由於確實這樣的:

selectedImage = [UIImage imageNamed:@"icon_72.png"]; 
[selectedImage release]; 
[selectedImage autorelease]; 
//objc[55403]: FREED(id): message autorelease sent to freed object=0x59b54c0 
//Program received signal: 「EXC_BAD_INSTRUCTION」. 

A ND所以如下:

selectedImage = [UIImage imageNamed:@"icon_72.png"]; 
[selectedImage autorelease]; 
[selectedImage release]; 
//objc[55264]: FREED(id): message release sent to freed object=0x592c9a0 
//Program received signal: 「EXC_BAD_INSTRUCTION」. 

所以確實這樣的:

selectedImage = [UIImage imageNamed:@"icon_72.png"]; 
[selectedImage autorelease]; 
[selectedImage autorelease]; 
//objc[55635]: FREED(id): message release sent to freed object=0x5b305d0 
//Program received signal: 「EXC_BAD_INSTRUCTION」. 
+0

像海報說的,你實際上是過度放出selectedImage,每當你調用一個類方法,它就會返回一個自動釋放的對象 – Daniel 2010-05-28 15:19:59

回答

11

-imageNamed:返回一個自動釋放的圖像,其中,作爲deanWombourne說,將在未來的某個時間自動發佈(具體時間未定)。

早在你習慣的時候就沒有被autoreleased的原因是-imageNamed也會緩存它返回的圖像。緩存保留圖像。

所以基本上,在保留週期是這樣的:

  • -imageNamed:叫,
    • 系統allocs和init的圖像 - 保留計數= 1;
    • 系統緩存圖像 - 保留計數= 2;
    • 系統autoreleases圖像並返回給你 - 保留計數= 1; (理論上,由於自動釋放池尚未釋放它,圖像仍然保留2個計數)。
  • 你在圖像上調用release--保留計數應該是0,並且對象應該被釋放。
  • 在未來的某個時間點(在運行循環結束時),自動釋放池應該釋放圖像,並且會因爲您釋放它而崩潰。

如果您不釋放它,緩存將繼續保留圖像直到它釋放它,例如發生內存警告時。因此,當您使用imageNamed獲取圖像時,它不會被釋放,直到緩存被清除。

希望這可以解決問題。

+0

啊,緩存位是一個很好的提示。有什麼地方可以瞭解緩存如何影響保留計數?即使我的程序不再保留UIImage對象,緩存是否有可能保留對象? – MrHen 2010-05-28 16:25:52

+3

您不應該關注保留計數的實際值。所有你應該關心的是,你從便捷方法中收到的對象不被你保留,所以你不應該釋放它。 如果您不希望圖像因任何原因被緩存,則應使用其他方法創建它們,例如-imageWithContentsOfFile:不緩存圖像(在該方法的文檔中指出)。您可以期望從imageWithContentsOfFile返回的圖像對象:被自動釋放並且不被緩存,並且將在運行循環結束時被釋放。 – Jasarien 2010-05-28 16:40:26

+0

「,它將在運行循環結束時釋放。」 - 如果你沒有保留它(即將其設置爲imageView的圖像,將其添加到數組等)。 – Jasarien 2010-05-28 16:45:53

8

奇數和奇怪的,是的。但不完全莫名其妙。這是我認爲正在發生的事情。

你是對的; imageNamed:返回一個自動釋放對象。這意味着它將在未來的某個時候發佈,所以你馬上釋放它並不會導致錯誤 - 發佈不是精神病,它不知道autorelease池也會釋放它!

如果你讓你的代碼運行autorelease池,最終會嘗試再次釋放它,然後然後你會得到你所期望的錯誤。

你實際上已經回答了我們自己的問題 - 你說這是絕對正確「時出現自動釋放應該打破」,當自動釋放時,它會破壞:)

其他的例子打破,因爲你通過直接調用它們或者做足夠的東西來觸發自動再生池運行併爲您調用發佈來強制發佈。 (你無法預測何時自動釋放池將運行,你可以知道,在運行中的循環某些時候,自動釋放的東西maight被釋放。)

+0

我的印象是,一旦程序返回到事件循環,autorelease池就會消失。這是不正確的?我可以將程序打開一段時間,永遠不會看到它崩潰。 – MrHen 2010-05-28 16:22:35

+1

是的autorelease池永遠不會保持「非常長」的東西。誠然,不保證它什麼時候會枯竭,但它不是你必須讓程序運行一段時間才能發現的東西。 Jasarien的回答給出了在這個特例中發生的事情的正確解釋。 – Felixyz 2010-05-28 17:18:18

0

你說, 「這應該打破」

selectedImage = [UIImage imageNamed:@"icon_72.png"]; 
[selectedImage release]; 

你錯了。

如果UIImage是一種類型的實例,你可能會寫,並從我們的Cocoa書籍中學習編寫,但是我們沒有編寫它,所以我們不應該猜測它的實現。

UIImage如何工作是一個實現細節,而不是您的關注。所有你知道的是,如果你遵循規則,那麼你應該能夠期望它的工作,我認爲這些規則現在被稱爲NARC,而你在這裏還沒有做過。如果你不正確地使用它們,任何地方都不能保證'破壞'。當你通過它們時,你不能指望被釋放的對象 - 這不是內存管理合同的一部分。

不是所有的Apple對象都像教科書類/實例一樣工作 - 實際上,對象可能被緩存,重用,回收或根本不是對象。

不用擔心,請遵守規則。

+0

我需要的實現細節,因爲我正在處理的應用程序正在推動iPhone/iPad的內存限制。我遇到這種行爲的原因是因爲UIImage對象的行爲與我預期的不同。其他答案中提供的信息讓我知道如何以更智能的方式處理UIImage。 – MrHen 2010-05-28 16:29:03

+0

對不起,但這些人是錯的。這與自動釋放或保留循環無關。如果框架已經緩存了圖像,那麼當它完成後,它們將取消緩存它,然後你會在某個隨機點崩潰,因爲你「智能地」處理了它。 – hooleyhoop 2010-05-28 16:36:30

+0

我討厭你說我錯了;) 你是正確的,因爲實現細節不必擔心,但在這種特殊情況下,即使不是直接的,問題**與保留計數有關因爲緩存保留了圖像。 – Jasarien 2010-05-28 16:42:48

0

當我創建了這個範疇:

@implementation UIImage (ReleaseChecks) 

+ (id)allocWithZone:(NSZone *)zone 
{ 
    id o = [super allocWithZone:(NSZone *)zone]; 
    NSLog(@"Image post-ALLOC: 0x%x", 
       (unsigned int)o); 
    return o; 
} 

- (id)autorelease 
{ 
    NSLog(@"Image pre-AUTORELEASE: 0x%x; Retain Count %u", 
       (unsigned int)self, 
       (unsigned int)[self retainCount] 
       ); 
    return [super autorelease]; 
} 

- (void)release 
{ 
    NSLog(@"Image pre-RELEASE: 0x%x\n; Retain Count %u", 
       (unsigned int)self, 
       (unsigned int)[self retainCount] 
       ); 
    [super release]; 
} 

- (void)dealloc { 
    NSLog(@"Image pre-DEALLOC: 0x%x\n; Retain Count %u", 
       (unsigned int)self, 
       (unsigned int)[self retainCount] 
       ); 
    [super dealloc]; 
} 

看來,當+ imageNamed分配-autorelease不叫:。但是,當我用+ imageNamed創建了一大堆這些內容,然後得到內存警告時,我可以看到它們全部釋放和釋放。這是在iPhone模擬器4.0上測試的。