【问题标题】:MKMapView still sending messages to delegate after it's superview has been de-alloc'edMKMapView 在它的超级视图被取消分配后仍然向委托发送消息
【发布时间】:2012-01-24 00:48:54
【问题描述】:

编辑:更改了标题。我当时不知道,但这是Why am I crashing after MKMapView is freed if I'm no longer using it?的复制品


这个问题与Why is object not dealloc'ed when using ARC + NSZombieEnabled 相似,但又足够不同,我认为值得把它扔在那里,以防有人理解并可以向我解释正在发生的事情。另一个问题可能是 XCode 错误,所以我认为这可能是相似的。

场景:

  1. RootViewController 有一个 tableView 显示一堆项目
  2. 选择一个单元格会显示一个模态detailViewController,其中包含另一个tableView
  3. detailViewController 中的一个表格单元格包含一个 MKMapView,显示项目的位置
  4. mapView.delegate = detailViewController
  5. 关闭模态detailViewController

此后不久,应用程序崩溃 b/c,MKMapViewmapView:viewForAnnotation: 发送到现在已解除锁定的 detailViewController。此崩溃在具有临时分发版本的用户设备上重现,因此该问题与 NSZombieEnabled 无关。

我能够通过添加以下内容来解决崩溃:

_mapView.delegate = nil;

到包含 mapView 的 tableViewCelldealloc 方法。

问题:为什么在释放单元格时需要将委托归零?当单元被解除分配时,似乎mapView 应该由 ARC 解除分配,而这是不必要的。 nil 代表是一种很好的做法,但我认为在这种情况下不需要这样做。

编辑:detailViewControllerUITableViewCells 的所有子视图都声明为(nonatomic, strong) 属性ala:

@property (nonatomic, strong)   MKMapView *         mapView;

编辑 2:我想我需要更好地阅读文档。 @fluchtpunkt 是正确的。以下是来自MKMapView 文档的相关信息:

在释放您设置了一个 MKMapView 对象之前 委托,记得将该对象的委托属性设置为 nil。一 你可以这样做的地方是你处理的 dealloc 方法 地图视图。

【问题讨论】:

  • 您能否为您的detailViewController 发布@strong/@weak 属性声明,尤其是MKMapView
  • 完成。我没有 @weak 属性,因为该应用程序针对 iOS 4.3+。 mapView 在自定义 UITableViewCell 中作为强属性。
  • 听起来和 pre-ARC 一样,正如我在这里描述的:stackoverflow.com/questions/2188098/…
  • 是的,是的。与 ARC 完全无关,只是我不知道的 MKMapView 的要求。希望我在发布之前看到你的问题。没有什么比(误)读文档更能让你不时谦虚的了。

标签: objective-c ios xcode4.2 automatic-ref-counting


【解决方案1】:

MKMapView 未使用 ARC 编译,因此 delegate 的属性仍声明为 assign 而不是 weak
来自MKMapView documentation

@property(nonatomic, assign) id<MKMapViewDelegate> delegate

来自Transitioning to ARC Release Notes

如果你需要管理资源而不是释放实例变量,你可以实现一个dealloc方法。您不必(实际上您不能)释放实例变量,但您可能需要在系统类上调用 [systemClassInstance setDelegate:nil] 和其他未使用 ARC 编译的代码。


对于系统类(NS*、UI*)的委托,您必须在解除分配委托对象时使用将委托设置为 nil 的“旧”规则。

所以在你的 detailViewController 中添加一个 dealloc 方法

- (void)dealloc {
    self.mapView.delegate = nil;
}

【讨论】:

  • 嗯,这就是我所做的(有点)。 mapView 在一个表格单元格中,所以当单元格被释放时,我现在设置_mapView.delegate = nil。但似乎仍然有点怀疑 b/c 我无法控制 tableCell 何时被解除分配与它的委托 (detailViewController) 何时被解除分配。似乎mapView 应该在它的单元格已经被释放时被释放,所以将它的委托设置为 nil 应该是多余的,不是吗?
  • 如果您不再使用该对象,ARC 已经为您发布了它。但是,系统类也可能保留了它。这总是会发生,因为您无法控制,您必须取消非 ARC 类的代表。如果 mapView 是单元格的一部分,您当然应该在单元格的 dealloc 中将委托设置为 nil。
  • 我理解,当被释放的对象可以明确地确保没有任何东西持有对它的 unsafe_unretained 引用时,这是有道理的。在此示例中,情况并非如此,因为即使 mapView 的委托 (detailViewController) 已被解除分配,也无法保证单元不会被另一个保持 mapView 的对象保留。想象一个例子,其中单元格被不同的类替换。无论如何,这似乎适用于这种情况,所以我可能会接受你的回答,但我不相信这会解决所有情况。谢谢。
  • 作为一个相关问题,您是否提倡对非 ARC 系统类的所有分配的委托属性进行此操作?例如,tableView 本身怎么样?当拥有tableView 的控制器被释放时,我是否应该明确设置tableView.dataSource = niltableView.delegate = nil?这绝对是 ARC 之前不需要的东西。
  • 最后,您的回答实际上得到了mapView 文档的确认。我会在问题的底部添加一些内容。我想我应该从那里开始。再次感谢。
【解决方案2】:

虽然确实应该将此类类的委托明确设置为nil,但在dealloc 中这样做已经太晚了。在viewDidUnload 期间,您已经失去了对地图视图的引用。您应该在viewDidUnload 之前执行self.mapView.delegate = nil(所以可能是viewWillDisappearviewDidDisappear

根据我的经验,只有 MKMapViewUIWebView 会这样。

【讨论】:

  • 这是不正确的。 viewDidUnload 并不总是被调用。您需要在这两个地方都将委托设为 nil。
  • 另外,在这种情况下这是不可能的。 mapView 位于 UITableCell 而不是视图控制器中。
  • 我的措辞是错误的,但我在谈论 viewDidUnload 作为视图控制器循环阶段。即使 viewDidUnload 未被调用,IBOutlets 和其他弱 UIView 引用也会在视图层次结构卸载后归零。 (除非您强烈引用您的观点,否则您可能一开始就不应该这样做)
  • 我会澄清的。感谢您对答案的补充。 WRT viewDidUnload,你也需要在那里设置mapView.delegate = nil。不正确的部分是说dealloc 为时已晚。你不能只依赖viewDidUnload,因为它并不总是被调用。
  • 只要您的self.mapView 到那时仍然有效。另外,我错过了你对fluchtpunkt 的评论,上面提到The mapView is in a custom UITableViewCell as a strong property. ,所以我提到了view*** 方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多