在这个区块中强烈捕获自我可能会导致保留周期

use*_*209 205 cocoa-touch objective-c retain avplayer automatic-ref-counting

如何在xcode中避免此警告.这是代码片段:

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
    current+=1;

    if(current==60)
    {
        min+=(current/60);
        current = 0;
    }

    [timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line
}];
Run Code Online (Sandbox Code Playgroud)

Tim*_*Tim 511

self这里的捕获是通过隐式属性访问进入的self.timerDisp- 你不能在一个强烈保留的块中引用self或属性.selfself

您可以通过self在访问timerDisp块内部之前创建弱引用来解决此问题:

__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                     queue:nil
                                usingBlock:^(CMTime time) {
                                                current+=1;

                                                if(current==60)
                                                {
                                                    min+=(current/60);
                                                    current = 0;
                                                }

                                                 [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
                                            }];
Run Code Online (Sandbox Code Playgroud)

  • 解决.改为使用它:__ innafe_unretained typeof(self)weakSelf = self; 感谢@Tim的帮助 (63认同)
  • 尝试使用`__unsafe_unretained`代替. (13认同)
  • 我没有在OP的代码中看到保留周期.该块不是由`self`强保留的,它由主调度队列保留.我错了吗? (8认同)
  • @erikprice:你没错.我将问题解释为主要是关于Xcode所呈现的错误("我如何在xcode中避免此警告"),而不是实际存在保留周期.你只是从提供的代码片段OP中说没有明显的保留周期是正确的. (3认同)

iiF*_*man 52

__weak MyClass *self_ = self; // that's enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
    if (!error) {
       [self_ showAlertWithError:error];
    } else {
       self_.items = [NSArray arrayWithArray:receivedItems];
       [self_.tableView reloadData];
    }
};
Run Code Online (Sandbox Code Playgroud)

还有一件非常重要的事情要记住:不要直接在块中使用实例变量,将其用作弱对象的属性,示例:

self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
        if (!error) {
           [self_ showAlertWithError:error];
        } else {
           self_.items = [NSArray arrayWithArray:receivedItems];
           [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
        }
 };
Run Code Online (Sandbox Code Playgroud)

并且不要忘记做:

- (void)dealloc {
    self.loadingCompletionHandler = NULL;
}
Run Code Online (Sandbox Code Playgroud)

如果您将传递任何对象未保留的弱副本,则会出现另一个问题:

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};
Run Code Online (Sandbox Code Playgroud)

如果vcToGo将被解除分配,然后这个块被解雇,我相信你会因为无法识别的选择器而崩溃到vcToGo_现在包含变量的垃圾箱.试着控制它.

  • 如果您也解释一下,这将是一个更强有力的答案。 (3认同)

War*_*shi 42

更好的版本

__strong typeof(self) strongSelf = weakSelf;
Run Code Online (Sandbox Code Playgroud)

创建对该弱版本的强引用作为块中的第一行.如果块在块开始执行时仍然存在并且没有回落到nil,则该行确保它在整个块的执行生命周期中持续存在.

所以整件事情都是这样的:

// Establish the weak self reference
__weak typeof(self) weakSelf = self;

[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                 queue:nil
                            usingBlock:^(CMTime time) {

    // Establish the strong self reference
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        [strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
    } else {
        // self doesn't exist
    }
}];
Run Code Online (Sandbox Code Playgroud)

我多次读过这篇文章.这是Erica Sadun关于 如何避免使用块和NSNotificationCenter时出现问题的优秀文章


Swift更新:

例如,在swift中,带有成功块的简单方法是:

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}
Run Code Online (Sandbox Code Playgroud)

当我们调用此方法并需要self在成功块中使用时.我们将使用[weak self]guard let功能.

    doSomeThingWithSuccessBlock { [weak self] () -> () in
        guard let strongSelf = self else { return }
        strongSelf.gridCollectionView.reloadData()
    }
Run Code Online (Sandbox Code Playgroud)

这种所谓的强弱舞蹈被流行的开源项目所使用Alamofire.

有关更多信息,请查看swift-style-guide


Chr*_*ter 15

在另一个答案中,蒂姆说:

你不能在一个自我强烈保留的区块内引用自我或自我属性.

这不是真的.只要你在某个时刻打破循环,你就可以这样做.例如,假设您有一个触发计时器,该计时器具有保留自身的块,您还可以自我保持对计时器的强引用.如果你总是知道你会在某个时刻破坏定时器并打破循环,那就完全没问题了.

就我刚才的情况而言,我对代码执行了此警告:

[x setY:^{ [x doSomething]; }];
Run Code Online (Sandbox Code Playgroud)

现在我碰巧知道clang只会在检测到方法以"set"开头时才会产生这个警告(另外还有一个我不会在这里提及的特殊情况).对于我来说,我知道有没有出现是一个保留循环的危险,所以我改变了方法名"useY:"当然,这可能不适合所有的情况,通常你会希望使用弱引用,但我认为值得注意我的解决方案,以防它帮助别人.


bsh*_*ley 6

很多时候,这实际上并不是一个保留周期

\n

如果你知道事实并非如此,你就不必将毫无结果的弱者带到这个世界上。

\n

苹果甚至通过他们的 API 向我们强制发出这些警告UIPageViewController,其中包括一个 set 方法(它触发这些警告\xe2\x80\x93,如其他地方提到的\xe2\x80\x93,认为你正在为一个块的 ivar 设置一个值)和一个完成处理程序块(您无疑会在其中引用自己)。

\n

以下是一些编译器指令,用于从该行代码中删除警告:

\n
#pragma clang diagnostic push\n#pragma clang diagnostic ignored "-Warc-retain-cycles"\n    [self.pageViewController setViewControllers:@[newViewController] direction:navigationDirection animated:YES completion:^(BOOL finished) {\n        // this warning is caused because "setViewControllers" starts with "set\xe2\x80\xa6", it's not a problem\n        [self doTheThingsIGottaDo:finished touchThePuppetHead:YES];\n    }];\n#pragma clang diagnostic pop\n
Run Code Online (Sandbox Code Playgroud)\n