3

我在使用發佈配置運行時遇到錯誤,這似乎是本地變量tmp的過早發佈。本地變量的ARC生命週期,過早發佈

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid index path for use with UITableView. Index paths passed to table view must contain exactly two indices specifying the section and row. Please use the category on NSIndexPath in UITableView.h if possible.'

相關代碼:

@property (nonatomic, strong) NSIndexPath *selectedCellIndexPath; 

... 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    if (_selectedCellIndexPath != nil && [_selectedCellIndexPath isEqual:indexPath]) {   
     self.selectedCellIndexPath = nil; 
     [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
    } else if (_selectedCellIndexPath != nil && ![_selectedCellIndexPath isEqual:indexPath]) { 

//--- problematic code 
     NSIndexPath *tmp = _selectedCellIndexPath; 
     self.selectedCellIndexPath = indexPath; 
     [tableView reloadRowsAtIndexPaths:@[tmp, _selectedCellIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
//--- problematic code 

    } else { 
     self.selectedCellIndexPath = indexPath; 
     [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
    } 
} 

我有一個印象,局部變量tmp應該在這裏有很強的借鑑意義或者我不好嗎?

順便說一句,更改代碼到

NSIndexPath *tmp = self.selectedCellIndexPath; 

或改變

@[tmp, _selectedCellIndexPath][NSArray arrayWithObjects:tmp,_selectedCellIndexPath,nil]解決問題。

這是什麼問題?

+0

我不知道。也許self.selectedCellIndexPath創建一個副本?區別在於self.selectedCellIndexPath會像[self selectedCellIndexPath]那樣調用getter方法。訪問_selectedIndexPath意味着使用或更改iVar本身,而不給予ARC正確管理內存的機會。一般來說,如果沒有getter或setter本身,你應該更喜歡使用setter/getters,而不是直接訪問iVar – 2013-03-03 15:18:20

回答

3

tmp是一個很強的參考文獻;當地人默認在ARC中。

鑑於你的兩個建議修復應該由所有權利是相同的結果(假設你不覆蓋setter或getterCellIndexPath做一些奇怪的getter),我猜你已經發現了一個ARC錯誤。 (我發現了其中的一些,所以它並不讓我感到意外。)

嘗試使用zombies turned on運行您的代碼。如果它告訴你你正在訪問一個釋放對象,我會說這是一個ARC錯誤。如果是這種情況,嘗試創建一個簡化的測試用例並提交一個bug是很好的。

編輯:

我認爲這是一個錯誤的ARC。問題似乎是在調用arrayWithObjects:count:之前將對象存儲到臨時(堆棧內)緩衝區中,但是在該緩衝區中它們不會被保留。在你的代碼中,這些對象在被添加到緩衝區後(在-O1或更高版本中)失去了最後的強引用,所以你得到了一個早期的dealloc。以下是通過引起在臨時緩衝區懸空引用的第一個對象(因爲分配導致的第一個對象被釋放)導致系統崩潰一個更簡單的測試用例:

NSObject *foo = [[NSObject alloc] init]; 
@[foo, (foo = [[NSObject alloc] init])]; 
+0

另外,我的猜測是,這個bug是由你的代碼直接引用有時和通過訪問其他倍。這不應該引起麻煩,但有點不尋常。 – 2013-03-03 16:03:39

+0

是的,有一條消息發送到釋放實例。我有一個簡單的測試案例(默認主/明細項目模板,沒有storyboard,只需粘貼我的代碼,然後使用發佈配置運行它)。我認爲發佈配置的代碼優化會搞砸一些東西。 – Mindaugas 2013-03-03 16:16:47

+0

@Mindaugas現在就試試。哇,你不是在開玩笑。只要看看拆卸; 1秒。 – 2013-03-03 16:37:24

-1

你做了運行 - >分析在你的代碼? Clang在指出潛在的ARC問題方面做得非常好(解釋了爲什麼會出現問題)。對待分析警告爲錯誤。另外,如果你不想讓ARC發佈它,你的'tmp'var也許應該是一個iVar。這可能並不總是最優雅的解決方案,但它告訴ARC,只要這個類實例存在,就堅持這個值。

您正在通過iVar引用訪問_selectedIndexPath,從而避免獲取者和設置者。這意味着您正在跳過ARC管理的調用以保留,發佈和自動釋放。

所以你應該使用'自我'。

基本上,你不能總是預測ARC會做什麼。它將嘗試在每種方法結束時進行清理。所以有時你需要給它一些提示,通過創建一個屬性來保留一些東西。有時我想念撥號/釋放自己....有時大聲笑

+1

本地變量默認情況下有'__strong'註釋,所以我不應該堅持他們 – Mindaugas 2013-03-03 16:19:05

+0

和運行 - >分析沒有問題 – Mindaugas 2013-03-03 16:22:11

+0

雖然不一定是真的。這個問題有時候會出現popover代碼。解決方法是將彈出窗口存儲在iVar中,並在完成後將其設置爲零。這個問題是我的建議的來源 – 2013-03-03 16:31:30