我刚刚调试了一个非常讨厌的UIViewController
泄漏,这样即使在调用dismissViewControllerAnimated
之后UIViewController也没有被释放。
我将问题追溯到以下代码块:
self.dataSource.doNotAllowUpdates = YES;
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
} completion:^(BOOL finished) {
self.dataSource.doNotAllowUpdates = NO;
}];
基本上,如果我调用performBatchUpdates
然后立即调用dismissViewControllerAnimated
,则UIViewController会泄露,并且dealloc
的{{1}}方法永远不会被调用。 UIViewController永远存在。
有人可以解释这种行为吗?我假设UIViewController
在一段时间间隔内运行,比如500毫秒,所以我假设在上述间隔之后,它会调用这些方法,然后触发dealloc。
修复似乎是这样的:
performBatchUpdates
请注意, self.dataSource.doNotAllowUpdates = YES;
__weak __typeof(self)weakSelf = self;
[self.collectionView performBatchUpdates:^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.collectionView reloadItemsAtIndexPaths:@[indexPath]];
}
} completion:^(BOOL finished) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf) {
strongSelf.dataSource.doNotAllowUpdates = NO;
}
}];
成员变量BOOL
是我添加的变量,它在执行performBatchUpdates调用时阻止任何类型的dataSource / collectionView更新。
我在网上搜索关于我们是否应该使用doNotAllowUpdates
中的weakSelf / strongSelf模式的讨论,但没有找到关于这个问题的具体内容。
我很高兴能够找到这个漏洞的底部,但我希望有一个更聪明的iOS开发人员向我解释我所看到的这种行为。
答案 0 :(得分:0)
这似乎是UICollectionView的一个错误。 API用户不应期望在执行任务之后保留单个运行块参数,因此防止参考周期不应成为问题。
UICollectionView应该在完成批量更新过程后清除对块的任何引用,或者批量更新过程中断(例如,通过从屏幕上删除集合视图)。
您已经亲眼看到即使在更新过程中将集合视图移出屏幕,也会调用完成块,因此集合视图应该将其对该完成块的任何引用取消 - 无论集合视图的当前状态如何,它都不会再被调用。
答案 1 :(得分:-1)
如您所知,当未使用weak
时,会创建保留周期。
保留周期是由self
强烈引用collectionView
引起的,而collectionView
现在强烈引用self
。
必须始终假设在执行异步块之前self
可能已被释放。要安全地处理这个问题,必须做两件事:
self
(或ivar本身)weakSelf
作为nunnull
传递之前,始终确认performBatchUpdates
存在
PARAM <强>更新强>
在- (void)logPerformBatchUpdates {
[self.collectionView performBatchUpdates:^{
NSLog(@"starting reload");
[self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];
NSLog(@"finishing reload");
} completion:^(BOOL finished) {
NSLog(@"completed");
}];
NSLog(@"exiting");
}
周围进行一些记录确认了很多:
starting reload
finishing reload
exiting
completed
打印:
- (void)breakIt {
// dispatch causes the view controller to get dismissed before the enclosed block is executed
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];
} completion:^(BOOL finished) {
NSLog(@"completed: %@", self);
}];
});
[self.presentationController.presentingViewController dismissViewControllerAnimated:NO completion:nil];
}
这表明在离开当前作用域后会触发完成块,这意味着它将异步调度回主线程。
您提到在执行批量更新后立即关闭视图控制器。我认为这是你问题的根源:
经过一些测试,我能够重新创建内存泄漏的唯一方法是在解雇之前调度工作。这是一个很长的镜头,但你的代码是偶然的吗?:
dealloc
上面的代码导致dismissViewController
未在视图控制器上调用。
如果您使用现有代码并简单地发送(或执行选择器::)之后worksheet_change
电话,您也可能会解决此问题。