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

我怎样才能避免在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强烈保留的块中引用selfself属性。

你可以通过创build一个对self的弱引用来解决这个问题,然后访问timerDisp内的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]]; }]; 
 __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]; } }; 

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

 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将被释放,然后这个块被解雇,我相信你会碰到无法识别的select器现在包含vcToGo_variables的垃圾。 试着去控制它。

更好的版本

 __strong typeof(self) strongSelf = weakSelf; 

创build一个强大的参考,弱版本作为您的区块的第一行。 如果块在开始执行时仍然存在,并且还没有回退到零,则此行确保在整个块的执行生命周期中该块持续存在。

所以整个事情会是这样的:

 // 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更新:

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

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

当我们调用这个方法,需要在成功块中使用self 。 我们将使用[weak self]guard let特性。

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

这个所谓的强弱舞蹈被stream行的开源项目Alamofire

欲了解更多信息,请访问swift-style-guide

Tim又回答说:

你不能在自我强烈保留的块中引用自我或自我属性。

这是不正确的。 只要你在某个时候打破了这个循环,你就可以做到这一点。 例如,假设你有一个计时器,这个计时器有一个保持自我的块,你也可以自我保持对计时器的强烈的引用。 如果你总是知道你会在某个时候破坏计时器并打破这个循环,那么这很好。

在我的情况下,我有这样的代码警告:

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

现在我碰巧知道,如果clang检测到方法是以“set”开头的(另一个特例,我不会在这里提到),那么clang只会产生这个警告。 对我来说,我知道没有保留循环的危险,所以我改变了方法名称为“useY:”当然,这可能不适合所有情况下,通常你会想使用弱引用,但我认为值得注意的是我的解决scheme,以帮助其他人。

在提高精确度和风格上增加两分钱。 在大多数情况下,你只能在这个块中使用一个或几个自己的成员,很可能只是更新一个滑块。 铸造self是矫枉过正。 相反,最好是明确的, 投射你真正需要的对象。 例如,如果它是UISlider*的一个实例,比如_timeSlider ,那么在块声明之前执行以下操作:

 UISlider* __weak slider = _timeSlider; 

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

完整的例子:

 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 ++一样,variables声明可以更好地从右向左读取。 以这种顺序声明SomeType* __weak variable自然地从右向左读取为: variable is a weak pointer to SomeType