[英]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
. 既然你提到了单元重用,我假设你的
Cell
是UITableViewCell
的子类。 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.