在此块强烈捕捉自我很可能会导致保留周期(capturing self strongly in thi

2019-07-18 19:24发布

如何避免在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
}];

Answer 1:

捕获self这里来与你的隐含属性访问self.timerDisp -你不能指self或性质上self从将被牢固地保持块内self

您可以通过创建一个弱引用来解决这个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]];
                                            }];


Answer 2:

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

而一个很重要的事情要记住:不要直接在块中使用实例变量,把它作为弱对象,样本的属性:

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
        }
 };

不要忘记这样做:

- (void)dealloc {
    self.loadingCompletionHandler = NULL;
}

另一个问题就会出现,如果你会通过不通过任何物体保持弱副本:

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};

如果vcToGo将被释放,然后这个块解雇我相信你会得到具有无法识别的选择崩溃的垃圾是包含vcToGo_变量现在。 尽量控制它。



Answer 3:

更好的版本

__strong typeof(self) strongSelf = weakSelf;

创建一个强引用到弱版本在你的块中的第一行。 如果当块开始执行,并没有回落到零自我仍然存在,这条线,确保其持续整个块的执行生存周期。

所以整个事情会是这样:

// 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
    }
}];

我已阅读这篇文章多次。 这是埃里卡丧盾上的优秀文章如何避免问题使用模块和NSNotificationCenter当


斯威夫特更新:

例如,在迅速与成功块一个简单的方法是:

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}

当我们调用此方法需要用self的成功块。 我们将使用[weak self]guard let功能。

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

这种所谓的强弱舞蹈是由流行的开源项目中使用Alamofire

欲了解更多信息请查看快捷式导向



Answer 4:

在另一个答案,蒂姆说:

你不能指自己或性质上从自我将由自我的强烈保留一个块中。

这是不完全正确。 这是确定你要做到这一点,只要你在某些时候打破这个循环。 例如,假设你有火灾有保留自我,你还保持一个强大的参考定时器自我块的定时器。 这是完全正常的,如果你总是知道你会破坏定时在某些时候和打破这种循环。

在刚才我的情况,我有这个警告,对于确实代码:

[x setY:^{ [x doSomething]; }];

现在,我碰巧知道,如果它检测方法,采用“设置”(这我就不在这里提一个其他特殊情况)将启动铛只会产生此警告。 对于我来说,我知道有没有出现是一个保留循环的危险,所以我改变了方法名“useY:”当然,这可能不适合所有的情况,通常你会希望使用弱引用,但我认为这值得一提我的解决方案的情况下,它可以帮助别人。



Answer 5:

很多时候, 这并不是一个真正的保留周期

如果你知道这是不是,你不必把无果而终weakSelves走向世界。

苹果甚至势力对我们这些警告与API他们UIPageViewController ,其中包括一组方法(触发这些警告,提到其他地方,想你的值设置为伊娃是块),并完成处理程序块(以你会无疑是指自己)。

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

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


Answer 6:

在提高精度和风格增加两分钱。 在大多数情况下,你将只使用一个或几个成员的self在这一块,最有可能只是为了更新滑块。 铸造self是矫枉过正。 相反,它是更好的是明确的, ,你真正需要的块内的物体。 举例来说,如果它的一个实例UISlider* ,说, _timeSlider ,只是做了块声明之前,如下:

UISlider* __weak slider = _timeSlider;

然后,只需使用slider块内。 从技术上讲,因为它缩小了潜在的保留周期一般只有你所需要的对象,而不是里面的所有对象,这是更精确的self

完整的示例:

UISlider* __weak slider = _timeSlider;
[_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
     queue:nil
     usingBlock:^(CMTime time){
        slider.value = time.value/time.timescale;
     }
];

此外,最有可能的对象被强制转换为一个弱指针已经是内弱指针self以及最小化或完全消除一个保留周期的可能性。 在上面的例子, _timeSlider实际上是存储为弱引用,例如一个属性:

@property (nonatomic, weak) IBOutlet UISlider* timeSlider;

在编码风格,与C和C ++方面,变量声明更好地从右至左读。 声明SomeType* __weak variable在此为了更自然地从右到左为读取: variable is a weak pointer to SomeType



Answer 7:

我最近遇到了这样的警告,想要好一点的理解。 有点试验和错误之后,我发现它从具有方法来启动或者用“添加”或“保存”起源。 Objective C的治疗方法名以“新”,“黄金”,作为等返回保留的对象,但并没有提到(我能找到)有关“添加”或“保存”任何东西。 不过,如果我以这种方式使用的方法名称:

[self addItemWithCompletionBlock:^(NSError *error) {
            [self done]; }];

我会看到在[自定]行警告。 然而,这并不:

[self itemWithCompletionBlock:^(NSError *error) {
    [self done]; }];

我会继续使用了“__weak __typeof(个体经营)weakSelf =自我”的方式来引用我的对象,但真的不喜欢这样做,因为它会混淆将来我和/或其他开发。 当然,我也不能使用“添加”(或“保存”),但因为它带走了该方法的含义是雪上加霜。



文章来源: capturing self strongly in this block is likely to lead to a retain cycle