【问题标题】:Using __block and __weak使用 __block 和 __weak
【发布时间】:2013-10-07 14:51:17
【问题描述】:

我已阅读此主题:What does the "__block" keyword mean?,其中讨论了 __block 的用途,但我对其中一个答案感到困惑。它说__block 用于避免保留循环,但它下面的 cmets 让我不确定。

我使用它是这样的:

 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 吗?这种方式看起来有什么明显的问题吗?

【问题讨论】:

  • 不,它可能说“__weak 用于避免保留周期”。
  • 实际上其中一个答案(赞成,但未接受)说“__block 有时用于避免保留周期”
  • 我不明白;使用__weak 是避免这种情况的方法。
  • 我也不是 - 因此这个线程要求澄清。可以同时使用吗?
  • 也许混淆源于在手动引用计数中,__block说明符确实导致对象not被保留,因此可以避免一个保留周期。这种行为在 ARC 中有所不同(在“过渡到 ARC 发行说明”中的某处进行了记录)。

标签: objective-c objective-c-blocks weak-references


【解决方案1】:

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

__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(手动引用计数)中并非如此,其中变量被引用而不被保留。

将其标记为__weak 导致变量不被保留,因此块直接引用它但不保留它。这是潜在的危险,因为如果块的寿命比变量长,因为它将引用垃圾内存(并且可能崩溃)。

这是来自clang doc的相关段落:

在 Objective-C 和 Objective-C++ 语言中,我们允许 __weak 说明符用于 __block 对象类型的变量。 [...]此限定符导致保留这些变量而不发送保留消息。如果块(或副本)超过此对象的生命周期,这会故意导致悬空指针

最后,__block 可用于避免强引用循环(也称为保留循环)的说法在 ARC 上下文中是完全错误的。由于在 ARC 中 __block 会导致变量被强引用,因此实际上更有可能导致它们。

例如,在 MRC 中,此代码会破坏保留周期

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

而要在 ARC 中获得相同的结果,您通常会这样做

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

【讨论】:

  • 所以,如果我没看错的话,一起使用 __block 和 __weak 是没有意义的,因为它们实际上做相反的事情(在 ARC 中)?
  • 没有。如果您想修改块中的对象,这可能是有意义的,但持有对它的引用会导致保留周期。我想不出一个很好的例子,但可能存在合法用途。这就是为什么它是允许的,即使可能不安全,正如文档所述。
  • "在 ARC 中,这会导致变量被自动保留" 保留变量是如何工作的?需要保持可访问的“变量”是指针*aString,而不是对象。如何保留指针?
  • 不确定我是否完全理解您的问题。据我所知,它会将retain 发送到aString
  • 如果我没记错的话,要发生保留循环,您必须直接或间接地引用 self 中的块(例如 self.block = block)。因此,仅通过在块内使用self 不会创建保留周期。如果您不存储块,则不需要weakSelf
【解决方案2】:

如果你想改变块中的变量值,你应该使用__block

例如:

__block BOOL result = NO;
dispatch_sync(dispatch_get_main_queue(), ^{
  ...
  result = YES;
  ...
});

如果你想避免保留循环,你应该使用__weak

例如:

__weak typeof(self) wself = self;
self.foobarCompletion = ^{
  ...
  wself.foo = YES;
  ...
};

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多