[英]Avoiding the “capturing self strongly in this block is likely to lead to a retain cycle” message
[英]capturing self strongly in this block is likely to lead to a retain cycle
如何在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
}];
捕获self
这里来与你的隐含属性访问self.timerDisp
-你不能指self
或性质上self
从将被牢固地保持块内self
。
您可以通过在访问块中的timerDisp
之前创建对self
的弱引用来解决此问题:
__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]];
}];
__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_
变量的垃圾箱。 试着控制它。
__strong typeof(self) strongSelf = weakSelf;
创建对该弱版本的强引用作为块中的第一行。 如果块在块开始执行时仍然存在并且没有回落到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
}
}];
我多次读过这篇文章。 这是Erica Sadun关于如何避免使用块和NSNotificationCenter时出现问题的优秀文章
例如,在swift中,带有成功块的简单方法是:
func doSomeThingWithSuccessBlock(success: () -> ()) {
success()
}
当我们调用此方法并需要在成功块中使用self
时。 我们将使用[weak self]
和guard let
功能。
doSomeThingWithSuccessBlock { [weak self] () -> () in
guard let strongSelf = self else { return }
strongSelf.gridCollectionView.reloadData()
}
这种所谓的强弱舞蹈被流行的开源项目Alamofire
。
有关更多信息,请查看swift-style-guide
在另一个答案中,蒂姆说:
你不能在一个自我强烈保留的区块内引用自我或自我属性。
这不是真的。 只要你在某个时刻打破循环,你就可以这样做。 例如,假设您有一个触发计时器,该计时器具有保留自身的块,您还可以自我保持对计时器的强引用。 如果你总是知道你会在某个时刻破坏定时器并打破循环,那就完全没问题了。
就我刚才的情况而言,我对代码执行了此警告:
[x setY:^{ [x doSomething]; }];
现在我碰巧知道clang只会在检测到方法以“set”开头时才会产生这个警告(另外还有一个我不会在这里提及的特殊情况)。 对我来说,我知道没有保留循环的危险,所以我将方法名称更改为“useY:”当然,这可能不适合所有情况,通常你会想要使用弱引用,但是我认为值得注意我的解决方案,以防它帮助别人。
很多时候, 这实际上并不是一个保留周期 。
如果你知道事实并非如此,那么你就不需要将无用的弱者带入这个世界。
Apple甚至通过API向他们的UIPageViewController
强制发出这些警告,其中包括一个set方法(触发这些警告 - 如其他地方所述 - 认为你正在为一个块的ivar设置一个值)和一个完成处理程序块(在你无疑会提到自己)。
这是一些编译器指令,用于从一行代码中删除警告:
#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
增加两美分,提高精度和风格。 在大多数情况下,您只能在此块中使用一个或几个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
。
我最近遇到了这个警告,想要更好地理解它。 经过一些试验和错误,我发现它源于一个方法以“添加”或“保存”开始。 Objective C将以“new”,“alloc”等开头的方法名称视为返回一个保留对象,但没有提及(我可以找到)关于“add”或“save”的任何内容。 但是,如果我以这种方式使用方法名称:
[self addItemWithCompletionBlock:^(NSError *error) {
[self done]; }];
我将在[自我完成]线上看到警告。 但是,这不会:
[self itemWithCompletionBlock:^(NSError *error) {
[self done]; }];
我将继续使用“__weak __typeof(self)weakSelf = self”方式来引用我的对象,但实际上不喜欢这样做,因为它会混淆未来的我和/或其他开发者。 当然,我也不能使用“添加”(或“保存”),但这更糟糕,因为它消除了方法的含义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.