局部变量的ARC生命周期,过早释放

时间:2013-03-03 13:33:39

标签: iphone ios objective-c memory-management automatic-ref-counting

运行发布配置时遇到错误,这似乎是本地变量tmp的过早发布。

*** 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]解决了这个问题。

这里会出现什么问题?

2 个答案:

答案 0 :(得分:3)

tmp是一个很好的参考;默认情况下,本地人在ARC。

鉴于你的两个建议修复应该是所有权利都是相同的结果(假设你没有覆盖setCellIndexPath的setter或getter做一些奇怪的事情),我猜你已经发现了一个ARC错误。 (我发现了其中的几个,所以这并不会让我感到惊讶。)

尝试使用zombies turned on运行代码。如果它告诉你正在访问一个解除分配的对象,我会说这是一个ARC错误。如果是这样的话,尝试创建简化的测试用例并提交错误会很好。

修改

我认为这是一个ARC错误。问题似乎是在调用arrayWithObjects:count:之前对象存储在临时(堆栈内)缓冲区中,但在缓冲区中它们不会被保留。在您的代码中,这些对象在被添加到缓冲区(在-O1或更高版本中)后会丢失其最后一个强引用,因此您可以获得早期的dealloc。这是一个更简单的测试用例,它通过引起对该临时缓冲区中第一个对象的悬空引用而导致崩溃(因为赋值导致第一个对象被释放):

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

答案 1 :(得分:-1)

你做过Run->分析你的代码吗? Clang非常好地指出了潜在的ARC问题(解释了为什么会出现问题)。将分析警告视为错误。

另外,如果您不希望ARC发布它,您的'tmp'var应该是iVar。这可能并不总是最优雅的解决方案,但它告诉ARC,“只要这个类实例存在,就保持这个值。”

您正在通过iVar引用访问_selectedIndexPath,避免使用getter和setter。这意味着您正在跳过ARC管理的调用以保留,释放和自动释放。

所以你应该使用'self'。

基本上,你无法预测ARC会做什么。它将尝试在每种方法结束时进行清理。因此,有时您需要通过创建属性来提示某些内容将被保留。有时我想念自己调用alloc / release ....有时候lol