如何正确allocate/initialize一个弱变量? (Swift)

How to properly allocate/initialize a weak variable? (Swift)

我正在尝试消除与 MKMapView 相关的内存泄漏。我认为主要问题是我创建整个项目时没有使用故事板作为一系列视图,我通过将 alpha 设置为 0 或将视图缩小到零高度来管理这些视图。我在 ViewController.swift 中初始化了一个 mapView:

class ViewController: UIViewController{

//Properties
let mapView = MKMapView()

} 

然后在另一个 .swift 文件中扩展 ViewController,例如:

extension ViewController {

private func setupMapViews(){

    //MAPVIEW:
    mapView.frame = CGRect(x: 0, y: view.frame.height, width: view.frame.width, height: 0)
    mapView.overrideUserInterfaceStyle = .dark
    mapView.mapType = .hybrid
    mapView.delegate = self

}

在我的应用程序的主视图上,我有一个按钮,它使用动画切换到此 mapView。在我 运行 模拟器时检查调试导航器,我看到当发生 segue 时,内存使用从大约 90 MB 跳到 160 MB。当我按下添加到 mapView 的完成按钮时,内存使用量保持在 160 MB 左右,告诉我存在内存泄漏。当转回主视图控制器时,我首先尝试简单地将它从超级视图中删除,但应用程序保持在 160 MB 左右,告诉我有一个保留周期。我尝试改为将 mapView 的声明更改为弱变量:

class ViewController: UIViewController{

//Properties
weak var mapView: MKMapView?

} 

然后在viewDidLoad()中初始化它

override func viewDidLoad() {
    super.viewDidLoad()
    let mapV: MKMapView? = MKMapView()
    self.mapView = mapV
}

但是现在当我尝试从我的主视图转到 mapView 时,它没有初始化,并且视图没有出现。我做错了什么?

感谢任何帮助,谢谢!

你问:

How to properly allocate/initialize a weak variable?

你应该:

  1. 使用局部变量创建对象:

    let mapView = MKMapView()
    
  2. 在您的局部变量超出范围之前(即在当前方法中),将其添加到您的视图层次结构中(以便您的视图层次结构保持对它的强引用):

    view.addSubview(mapView)
    
  3. 更新您的 属性 以维护对此新实例化的 MKMapView.

    weak 引用
    self.mapView = mapView
    
  4. 您稍后可以使用以下方法从视图层次结构中删除地图视图:

    mapView?.removeFromSuperview()
    

    当你这样做时,地图视图将不再有任何强引用,weak 属性 将自动设置为 nil,你的记忆将被恢复。

因此,如果您想在用户点击按钮时添加 MKMapView,请将上述步骤移至您的按钮处理程序(或此调用的方法)而不是 viewDidLoad。如果您在没有第 2 步的情况下将此代码留在 viewDidLoad 中,MKMapView 将立即被释放。


几点观察:

  1. 诊断内存消耗时,不要依赖第一次迭代的结果(其中会包括缓存期间使用的内存),而是关注后续迭代。

    在快速测试中,运行在 Xcode“调试导航器”中显示的 50mb 应用程序中,显示地图时它跳到 120mb,当显示地图时下降到 85mb带有地图的视图控制器被解雇了。然后,第二次向视图控制器显示地图时,再次跳到 120mb,解雇后又回到 85mb。并且它继续使用这种模式,以便随后显示和关闭带有地图的视图控制器。因此,有很多缓存正在进行(价值约 35mb),但在后续启动中没有持续内存增长意味着没有(实质性)泄漏。

    分配工具(“Product” » “Profile” » “Allocations”)更清楚地说明了这一点,在这里我三次显示和关闭地图视图:

  2. 让我们假设您进行了上述诊断并确认您在每次迭代中都在丢失内存,而不仅仅是遇到缓存行为。

    然后我会 运行 调试器中的应用程序,关闭应用程序中的地图视图,然后使用 Xcode 的“调试内存图”功能找出是什么让一个强参考。在这里,我在调试导航器中搜索 MKMapView,它向我显示了图表,其中一条白线返回到相关的视图控制器。 (我忽略了灰色的线条,因为它们显示了 weak/unowned 个引用。)

    有关“调试内存图”功能的更多信息,请参见

  3. 您询问问题是否是因为您没有使用视图控制器或故事板。号

    例如,我重复了上面的例子,但是这次调用了 mapView.removeFromParent(并确保我代码中的 mapView 引用是 weak),你可以看到在地图视图的最后一个强引用被删除的一秒钟内(即标记为“删除”的紫色路标,我从视图层次结构中删除了地图视图,只有最终的 weak 引用),内存已恢复:

    现在,我在显示视图控制器和关闭它的地方有路标,但您可以看到这不相关。当我删除所有对地图视图的引用时(甚至在关闭视图控制器之前),内存已正确恢复。

最重要的是,问题不在于您到目前为止与我们共享的代码。你有一些东西保持对地图视图的强引用,如果没有 MCVE,我们将无法帮助你。但希望上面提供了一些诊断工具(尤其是“调试内存图”功能)。

问题可能在于 (a) 您如何从视图层次结构中删除地图视图;或 (b) 地图视图与其子对象(例如注释、注释视图等)之间的某些强引用循环。但我们没有足够的信息来诊断。