11

我正在使用NSOperationQueue和排队NSOperationBlocks。现在,块对块中的任何实例都有强引用,调用对象也对块有强引用,因此建议执行以下操作:

__weak Cell *weakSelf = self;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        UIImage *image = /* render some image */
        /* what if by the time I get here self no longer exists? */
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [weakSelf setImageViewImage:image];
        }];
    }];
    [self.renderQueue addOperation:op];

所以,我的问题是,假设当图像完成渲染并且该行返回时,该Cell对象不再存在(它已被释放,可能是由于单元重用,这有点难以正式化)。当我去访问[weakSelf setImageViewImage:]时,这会导致EXC_BAD_ACCESS错误吗?

目前我正在尝试追踪我的问题的原因是什么,我认为这可能与此有关。

4

2 回答 2

18

所以,__weak是一个归零弱参考。这意味着在您的操作过程中,self可能确实会被释放,但对它的所有弱引用(即weakSelf)将被清零。这意味着[weakSelf setImageViewImage:image]只是向 发送消息nil,这是安全的;或者,至少,它不应该导致EXC_BAD_ACCESS. (顺便说一句,如果你有资格weakSelfas __unsafe_unretained,你最终可能会向一个释放的对象发送消息。)

所以,我怀疑向__weak引用发送消息会导致崩溃。如果你想确保它self在你的操作过程中仍然存在,你可以在块范围内获得对弱的强引用:

__weak Cell *weakSelf = self;

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    Cell *strongSelf = weakSelf; // object pointers are implicitly __strong
    // strongSelf will survive the duration of this operation.
    // carry on.
}];
于 2012-07-22T19:33:06.643 回答
8

如果你不使用weak,你正在创建一个保留循环,但是一旦块完成执行,循环就会被打破。我可能不会在这里使用弱。

无论如何,您可以向 发送任何消息nil,它将被忽略。因此,如果weakSelf变量nil因为Cell对象被释放而被设置为,则setImageViewImage:消息将默默地不做任何事情。它不会崩溃。

由于您提到单元重用,我认为您CellUITableViewCell. 在这种情况下,您的示例代码存在严重问题。 UITableViewCells 通常不会被释放。它们被放入单元重用队列。所以你的weakSelf变量不会被归零,因为弱引用只有在对象实际被释放时才会被归零。

到该[weakSelf setImageViewImage:image]行运行时,该单元格可能已被重新用于表示表中的不同行,并且您在单元格中放置了错误的图像。您应该将图像渲染代码移出Cell类,移到表视图的数据源类中:

- (void)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    Cell *cell = // get a cell...

    [self startLoadingImageForIndexPath:indexPath];
    return cell;
}

- (void)startLoadingImageForIndexPath:(NSIndexPath *)indexPath {
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        UIImage *image = [self renderImageForIndexPath:indexPath];
        dispatch_async(dispatch_get_main_queue(), ^{
            Cell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
            [cell setImageViewImage:image];
        });
    }];
    [self.renderQueue addOperation:op];
}
于 2012-07-22T19:53:38.057 回答