如何在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
}];
__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];
}
};
有一件非常重要的事情要记住:
不要直接在block中使用实例变量,使用它作为弱对象的属性,示例:
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;
}
如果传递not retain by anyone对象的弱副本,则会出现另一个问题:
MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
[vcToGo_ doSomePrecessing];
};
如果vcToGo将被释放,然后这个块被发射,我相信你会崩溃,无法识别的选择器到一个垃圾,其中包含vcToGo_变量现在。试着控制它。
在提高精度和风格上增加了两分钱。在大多数情况下,你只会在这个块中使用一个或两个self成员,很可能只是更新一个滑块。施展自我是矫枉过正。相反,更好的方法是显式地强制转换块中真正需要的对象。例如,如果它是UISlider*的实例,比如_timeSlider,只需在块声明之前执行以下操作:
UISlider* __weak slider = _timeSlider;
然后在块内使用滑块。从技术上讲,这更精确,因为它将潜在的保留周期缩小到只需要的对象,而不是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是指向SomeType的弱指针。
在另一个回答中,蒂姆说:
您不能从将被self强保留的块中引用self或self上的属性。
这并不完全正确。只要你在某个时候打破了这个循环,你就可以这样做。例如,假设你有一个触发的定时器,它有一个保留self的块,你也在self中保留一个对定时器的强引用。如果你总是知道你会在某个时候破坏计时器并打破循环,这是完全没问题的。
在我刚才的例子中,我有这样的代码警告:
[x setY:^{ [x doSomething]; }];
现在我碰巧知道clang只有在检测到以“set”开头的方法时才会产生这个警告(还有一种特殊情况,我不会在这里提到)。对我来说,我知道存在保留循环的危险,所以我将方法名称更改为“useY:”当然,这可能不适用于所有情况,通常您会希望使用弱引用,但我认为值得注意我的解决方案,以防它有助于其他人。
__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];
}
};
有一件非常重要的事情要记住:
不要直接在block中使用实例变量,使用它作为弱对象的属性,示例:
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;
}
如果传递not retain by anyone对象的弱副本,则会出现另一个问题:
MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
[vcToGo_ doSomePrecessing];
};
如果vcToGo将被释放,然后这个块被发射,我相信你会崩溃,无法识别的选择器到一个垃圾,其中包含vcToGo_变量现在。试着控制它。
这里对自我的捕捉是伴随着你对自我的隐式属性访问。timerDisp -你不能从一个将被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]];
}];
我最近遇到了这个警告,想更好地理解它。经过一些尝试和错误之后,我发现它起源于以“add”或“save”开头的方法。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”的方式来引用我的对象,但真的不喜欢这样做,因为它会混淆未来的我和/或其他开发人员。当然,我也可以不使用“添加”(或“保存”),但这更糟糕,因为它带走了方法的意义。