简体   繁体   English

块内的弱引用

[英]Weak references inside a block

I'm using an NSOperationQueue and queuing up NSOperationBlocks . 我正在使用NSOperationQueue并排队NSOperationBlocks Now, blocks have a strong reference to any instances in the block, and the calling object also has a strong hold on the block, so it has been advised to do something like the following: 现在,块具有对块中任何实例的强引用,并且调用对象也对块具有强大的保持,因此建议执行如下操作:

__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];

So, my question is, let's say that by the time the image finishes rendering and that line comes back, the Cell object no longer exists (it has been deallocated, possibly due to cell reuse, which is a bit difficult to formalize). 所以,我的问题是,让我们说当图像完成渲染并且该行返回时, Cell对象不再存在(它已被解除分配,可能是由于单元重用,这有点难以形式化)。 When I go to access [weakSelf setImageViewImage:] , will that cause a EXC_BAD_ACCESS error? 当我去访问[weakSelf setImageViewImage:] ,会导致EXC_BAD_ACCESS错误吗?

Currently I'm trying to trace what the cause of my problem is, and I'm thinking it might have something to do with this. 目前我正在试图追踪问题的原因,我认为这可能与此有关。

So, __weak is a zeroing weak reference. 所以, __weak weak是归零的弱引用。 What this means is that during your operation, self may indeed be deallocated, but all weak references to it (namely weakSelf ) will be zeroed out. 这意味着在你的操作过程中, self确实可能被释放,但是对它的所有弱引用(即weakSelf )都将被清零。 This means that [weakSelf setImageViewImage:image] is just sending a message to nil , which is safe; 这意味着[weakSelf setImageViewImage:image]只是向nil发送消息,这是安全的; or, at least, it shouldn't cause an EXC_BAD_ACCESS . 或者,至少它不应该导致EXC_BAD_ACCESS (Incidentally, if you had qualified weakSelf as __unsafe_unretained , you might end up sending messages to a freed object.) (顺便说一句,如果您将合格的weakSelf视为__unsafe_unretained ,则最终可能会向已释放的对象发送消息。)

So, I doubt that sending a message to a __weak reference is causing a crash. 所以,我怀疑向__weak引用发送消息导致崩溃。 If you want to ensure that self survives for the length of your operation, you can get a strong reference to the weak one in the block scope: 如果你想确保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.
}];

If you don't use weak, you're creating a retain cycle, but the cycle will be broken as soon as the blocks have finished executing. 如果你不使用weak,你就是在创建一个保留周期,但是一旦块完成执行,周期就会被打破。 I probably wouldn't bother using weak here. 我可能不会在这里使用弱者。

Anyway, you can send any message to nil , and it will be ignored. 无论如何,你可以发送任何消息给nil ,它将被忽略。 So if the weakSelf variable gets set to nil because the Cell object is deallocated, the setImageViewImage: message will silently do nothing. 因此,如果因为取消分配Cell对象而将weakSelf变量设置为nil ,则setImageViewImage:消息将无声地执行任何操作。 It won't crash. 它不会崩溃。

Since you mention cell reuse, I presume your Cell is a subclass of UITableViewCell . 既然你提到了单元重用,我假设你的CellUITableViewCell的子类。 In that case, your example code has a serious problem. 在这种情况下,您的示例代码有一个严重的问题。 UITableViewCell s normally don't get deallocated. UITableViewCell通常不会被释放。 They get put on the cell reuse queue. 它们被放在单元重用队列中。 So your weakSelf variable will not get zeroed, because a weak reference only get zeroed when the object is actually deallocated. 因此,您的weakSelf变量不会被置零,因为弱对象仅在对象实际解除分配时才会被置零。

By the time the [weakSelf setImageViewImage:image] line gets run, the cell may have been reused to represent a different row in your table, and you're putting the wrong image in the cell. [weakSelf setImageViewImage:image]行运行时,单元格可能已被重用以表示表格中的不同行,并且您将错误的图像放入单元格中。 You should move your image-rendering code out of the Cell class, into your table view's datasource class: 您应该将图像渲染代码从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];
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM