关于C#:使用__block和__weak

Using __block and __weak

我读过这篇文章:"block"关键字是什么意思?它讨论了__block的用途,但我对其中一个答案感到困惑。它说__block用于避免保留周期,但下面的评论让我无法确定。

我使用它的方式如下:

1
2
3
4
5
 self.someProperty = x; //where x is some object (id)
 __block __weak VP_User *this = self;

 //begin a callback-style block
     this.someProperty = nil;

我需要同时使用__block__weak吗?这个样子有什么明显的问题吗?


__block是存储限定符。它指定变量应该由块直接捕获,而不是复制它。这在需要修改原始变量的情况下很有用,如下例所示

1
2
3
4
5
__block NSString *aString = @"Hey!";
void(^aBlock)() = ^{ aString = @"Hello!" }; // without __block you couldn't modify aString
NSLog(@"%@", aString); // Hey!
aBlock();
NSLog(@"%@", aString); // Hello!

在ARC中,这会导致变量被自动保留,以便在块实现中安全地引用它。在前面的示例中,当在块上下文中捕获时,aString将发送一条retain消息。

但这在MRC(手动参考计数)中并不成立,在该模型中,变量被引用而不被保留。

标记为EDOCX1[1]会导致变量不被保留,因此块直接引用它,但不保留它。这是潜在的危险,因为在这种情况下,块的寿命比变量长,因为它将引用垃圾内存(并且可能崩溃)。

这是申报文件的相关段落:

In the Objective-C and Objective-C++ languages, we allow the __weak specifier for __block variables of object type. [...] This qualifier causes these variables to be kept without retain messages being sent. This knowingly leads to dangling pointers if the Block (or a copy) outlives the lifetime of this object.

最后,可以使用__block来避免强参考周期(aka retain cycles)的说法在ARC上下文中显然是错误的。由于在arc EDOCX1中(0)导致变量被强引用,实际上更可能导致变量被强引用。

例如,在MRC中,该代码打破了保留周期。

1
2
3
4
__block typeof(self) blockSelf = self; //this would retain self in ARC!
[self methodThatTakesABlock:^ {
    [blockSelf doSomething];
}];

然而,为了在ARC中获得相同的结果,通常需要

1
2
3
4
__weak typeof(self) weakSelf = self;
[self methodThatTakesABlock:^ {
    [weakSelf doSomething];
}];


如果要更改块中的变量值,应该使用__block

例如:

1
2
3
4
5
6
__block BOOL result = NO;
dispatch_sync(dispatch_get_main_queue(), ^{
  ...
  result = YES;
  ...
});

如果要避免保留周期,应该使用__weak

例如。:

1
2
3
4
5
6
__weak typeof(self) wself = self;
self.foobarCompletion = ^{
  ...
  wself.foo = YES;
  ...
};

如果有需要,可以将它们组合起来。