2012-05-29 20 views
0

我有一個問題,ARC和塊,但已解決問題。不幸的是,我不知道究竟發生了什麼,希望更多地瞭解我的情況。使用塊和ARC導致「消息發送到解除分配的實例」錯誤

我本來是做這個

for(__block id<Foo> object in objects) { 
    foo download:someParm 
     success:^{ 
      object.state = StateNewState; 
     } 
    ]; 
} 

這造成了一個失衡的保留代碼。當一個對象被訪問並且被認爲已經被釋放時發生崩潰。我寫了一個實現並使用「copy」屬性的類來創建一個successBlock屬性,該屬性保存了傳遞給下載函數成功參數的塊。我用下面的代碼替換了這個代碼

for(id<Foo> object in objects) { 
    foo download:someParm 
     success:^(id<Foo> successObject){ 
      successObject.state = StateNewState; 
     } 
    ]; 
} 

沒有更多的釋放對象錯誤,但我還沒有運行儀器來檢查我是否沒有泄漏。一些如何使用__block導致對象被釋放太多次,我不知道爲什麼。我會繼續研究這個問題的原因,但我認爲這對其他人來說會是一個有趣的問題。

我想可能值得注意的是,對象數組是一個自動釋放數組,它是在本文前面寫下的代碼行中創建的。不要以爲這很重要,但我認爲我只是通過那裏。我放在這篇文章中的代碼並不是確切的代碼,因爲我正在使用這個工作,並且在那裏有一堆絨毛。但是for循環中沒有創建其他對象。

當應用程序崩潰時,它運行下載,然後運行回調,順便說一句,我使用ASIHttp。當我嘗試再次下載它時,它將運行並且不會調用回調,因爲對象已被釋放並且委託沒有被使用。在此之後,當對象被包含指向我們崩潰的對象的指針的字典訪問時。

+0

我讀的文檔,並提醒使用__block用於不增加實例的實例變量的保留計數,通過使用__block指針自我引用內的變量使用塊。 我在想,如果使用__block不會增加對象的保留計數,那麼它是否也會是真的?id爲對象?因此當塊被釋放時造成保持不平衡導致對象也被釋放? – Biclops

回答

4

Blocks Programming Topics說:塊內

使用實例變量將導致自身被保留的對象 。如果您希望替代 特定對象變量的此行爲,可以使用__block存儲 類型修飾符標記它。

如果您使用ARC,則會在複製塊並隨後發佈塊時自動保留和釋放對象變量。

因此,如果您不使用__block,我想您會發現您的變量正在爲您保留。例如,這似乎爲我工作:

NSMutableArray *array = [[NSMutableArray alloc] init]; 

// add two custom objects to that array 

[array addObject:[[MyObject alloc] initWithText:@"One" number:1]]; 
[array addObject:[[MyObject alloc] initWithText:@"Two" number:2]]; 
[array addObject:[[MyObject alloc] initWithText:@"Three" number:3]]; 

// now replace the text field in each of the three objects with the word "Done" 

for (MyObject *object in array) 
{ 
    [self blockTestInvocation:^{ 
     NSLog(@"%s %@", __FUNCTION__, [NSDate date]); 

     object.text = @"Done"; 
    }]; 
} 

我發現存在或不存在的__block沒有產生實質性的影響,當我blockTestInvocation同步調用傳遞塊,但是當我把它調用(在我的數組和對象本來會被釋放之後),不存在__block確保對象被保留,從而防止可怕的「發送到釋放實例的消息」(並且沒有泄漏)。但是對於__block,該對象不會被保留,因此可能會在代碼塊嘗試引用它時解除分配。

+0

感謝您的幫助。我一定會留意__block如何防止對象被自動保留。 – Biclops

1

兩件事:

1)某些東西不能說明你所描述的內容。你說你把這些對象放在一個自動釋放的數組中,這大概是臨時存儲以用於運行循環。然後你的塊回調在這些對象上設置了一些狀態,除非別的東西也保留它們,否則這將毫無意義。所以你的問題在於別的東西 - 無論創建那些對象應該在需要它們時保留它們 - 大概足夠長以觀察狀態改變。如果是的話,你不會得到EXC_BAD_ACCESS錯誤。

2)你不需要在循環的__block預選賽。它基本上告訴編譯器您的塊可能會爲該引用分配一個新對象,因此它需要取消引用該變量。但是你的街區並沒有那樣做。你只是發送消息給對象。如果您不使用__block限定符,則您的塊將獲取該值的const副本 - 該值是指向您的對象的指針。然後當你做object.state = StateNewState時,你發送一個setState:newState消息給該指針處的對象。所以這應該很好地工作:

for(id<Foo> object in objects) { 
    foo download:someParm 
     success:^{ 
      object.state = StateNewState; 
     } 
    ]; 
} 
+1

我同意這一觀察,即爲你不打算保持的對象設置狀態有點奇怪。我認爲這只是一個超簡化的例子,而不是一個實際的,真實的場景。但是,如果您已經否認了啓動下載的觀點並且在下載完成時視圖被駁回,那麼我可以想象出這類問題將會體現出來。 – Rob

+0

我不應該把那個位放在那裏。忽略這一點。 – Biclops

+0

對數字2有很好的洞察力。我在一天結束時腦死亡,但你說的是對的。不知道我在想什麼。 – Biclops

相關問題