【问题标题】:Strong references and UIView memory issues强引用和 UIView 内存问题
【发布时间】:2017-03-10 15:11:22
【问题描述】:

我正在处理一些释放问题,并且可能是无法弄清楚的强引用或循环引用。我有三个UIViews 实例化如下:

有一个主要的ViewController,我在故事板中添加了一个UIViewUIView 在类中有一个weak 出口,例如:

class ViewController : UIViewController {

    //MARK: - outlets
    @IBOutlet weak var firstView: FirstUiview!

} 

第二个UIView 以编程方式作为子视图添加到第一个视图中,例如:

class FirstUiview : UIView { 

        //creating an instance of secondUiView 
        lazy var mySecondView: SecondViewClass = {
          let dv = SecondViewClass()
          dv.backgroundColor = UIColor.red
          return dv
        }()


        //sometime later by clicking on a button 
        self.addSubview(mySecondView)

        //a button will be tapped to remove mySecondView; 
        //later will be called at some point upon tapping:

       func removingSecondViewByTapping()  {
         if mySecondView.isDescendant(of: self) {
           mySecondView.removeFromSuperview()
        }
       }

} 

现在SecondViewClass 是:

class SecondViewClass : UIView { 

      //in this class I create bunch of uiview objects like below: 
      lazy var aView : UIView = {
        let hl = UIView()
        hl.tag = 0
        hl.backgroundColor = UIColor.lightGray
        return hl
      }()

      self.addSubview(aView) //... this goes on and I add other similar views the same way.

        //creating an instance of thirdView
        var let thirdView = UIView() 
        self.addSubview(thirdView)

} 

现在,如果用户点击按钮删除 mySecondView,然后在其他时间再次添加它(仍然在同一个 ViewController 中),我希望 mySecondView 的所有子视图都已被释放并消失,但它们是都在那里。如果有人能指出我在哪里保持强引用或者是否存在循环引用问题,我将不胜感激?或者别的什么?

【问题讨论】:

  • 为什么你会期望第二个视图的子视图消失?从其父视图中删除 mySecondView 并不会使其从自身中删除其所有子视图。
  • @dan 哦,等等,我以为 view.removeFromSuperview 会释放并销毁包含其子视图的视图,除非它的子视图引用了所持有的东西,不是吗?如果没有,你能不能指导我如何完全删除发送视图,包括它的子视图?

标签: ios swift memory-management weak-references dealloc


【解决方案1】:

您有两个对视图的强引用,即您的自定义属性和调用addSubview 时建立的视图层次结构引用。当您从视图层次结构中删除视图时,您的类本身仍然具有对它的强引用。

您可以通过将引用设为可选来解决此问题,并且当您调用 removeFromSuperview 时,还可以手动将您的引用设置为 nil。或者,也许更简单,您可以通过使用weak 引用来解决这个问题,让视图层次结构为您维护强引用。而且因为您的自定义属性是weak,当您将其从视图层次结构中移除(从而消除对它的唯一强引用)时,您的weak 引用将自​​动变为nil

class FirstView: UIView {

    weak var secondView: SecondView?       // note the `weak` reference, which is obviously an optional

    //sometime later by clicking on a button

    func doSomething() {
        let subview = SecondView()
        subview.backgroundColor = .red
        self.addSubview(subview)
        secondView = subview
    }

    // a button will be tapped to remove secondView;
    // later will be called at some point upon tapping ...

    func removingSecondViewByTapping()  {
        secondView?.removeFromSuperview()
    }
}

【讨论】:

  • 感谢您的回复。它确实看起来是正确的解释和答案。当我试图在这里简化它时,我担心这个问题比我更深。虽然答案很明确!我试过这个没有锁。我的第三个视图有一个父视图符合它的协议,这可能是另一个问题吗?我已经确定它是一个弱协议委托,即类似于stackoverflow.com/a/24104371/5601401
  • 是的,未能使用弱协议也可能是个问题。因此,如果您已修复上述问题并确保使用weak 委托引用,但您仍然没有看到您的视图被释放,您需要诊断是什么保留了强引用。使用“调试内存图”(请参阅​​stackoverflow.com/a/30993476/1271826)有助于跟踪您可能遇到的任何其他问题。
  • “致命错误:在展开可选值时意外发现 nil”在初始化为 weak 的对象集后执行的第一行上。每一次。
  • @BrandonA - 如果你注意到,在上面,我将视图设置为局部变量,将其添加为子视图(建立强关系),然后我才设置 weak伊瓦尔如果您尝试使用快捷方式而不使用局部变量,或者如果您在离开函数之前没有将其添加到视图层次结构中,您可能会收到您所描述的错误。我建议您创建一个gist,向我们展示您的尝试。但以上是一种行之有效的技术。
猜你喜欢
  • 2016-11-15
  • 1970-01-01
  • 2020-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-03-19
相关资源
最近更新 更多