在这个区块强烈地捕获自我很可能会导致一个保留周期
我怎样才能避免在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
属性。
你可以通过创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
。