如何在 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
}];
timerDisp
是类的属性吗?
player(AVPlayer object)
和 timerDisp(UILabel)
?
此处对 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;
创建对该弱版本的强引用作为块中的第一行。如果当块开始执行时 self 仍然存在并且没有回退到零,则此行确保它在整个块的执行生命周期中持续存在。
所以整个事情会是这样的:
// 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 在 How To Avoid Issues When Using Blocks And 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
typeof(self) strongSelf = self;
(而不是 __weak)然后在使用后在块中说 strongSelf = nil;
怎么办?我看不到您的示例如何确保在块执行时 weakSelf 不为零。
在另一个答案中,蒂姆说:
您不能在将由 self 强烈保留的块中引用 self 或 self 上的属性。
这并不完全正确。只要您在某个时候打破循环,您就可以这样做。例如,假设您有一个触发的计时器,它有一个保留 self 的块,并且您还在 self 中保留了对计时器的强引用。如果你总是知道你会在某个时候破坏计时器并打破循环,这很好。
就我刚才的情况而言,我收到了以下代码的警告:
[x setY:^{ [x doSomething]; }];
现在我碰巧知道,只有当它检测到方法以“set”开头时,clang 才会产生这个警告(以及另一种我不会在这里提到的特殊情况)。对我来说,我知道没有保留循环的危险,所以我将方法名称更改为“useY:”当然,这可能并不适用于所有情况,通常你会想要使用弱引用,但是我认为值得注意我的解决方案,以防它帮助其他人。
很多时候,这实际上并不是一个保留周期。
如果你知道它不是,你就不需要把毫无结果的弱者带到这个世界上。
Apple 甚至通过其 UIPageViewController
的 API 将这些警告强加给我们,其中包括一个 set 方法 (它会触发这些警告——正如在其他地方提到的——认为你正在为一个块的 ivar 设置一个值) em> 和一个完成处理程序块(您无疑会在其中引用自己)。
这是一些编译器指令,用于从那一行代码中删除警告:
#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 done] 行看到警告。但是,这不会:
[self itemWithCompletionBlock:^(NSError *error) {
[self done]; }];
我将继续使用“__weak __typeof(self)weakSelf = self”方式来引用我的对象,但我真的不喜欢这样做,因为它会混淆未来的我和/或其他开发人员。当然,我也不能使用“添加”(或“保存”),但这更糟,因为它带走了方法的含义。
__unsafe_unretained
。self
不会强保留该块,它由主调度队列保留。我错了吗?