据我所知,我正经历着一个无主的自我的泄漏,在这种情况下,不应该有泄漏。让我举一个例子,它有点做作,所以请容忍我,我已经尽力做了最简单的例子。
假设我有一个简单的视图控制器,它在viewDidLoad上执行一个闭包:

class ViewController2: UIViewController {

    var onDidLoad: (() -> Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        onDidLoad?()
    }
}

还有一个类ViewHandler,它拥有这个视图控制器的一个实例,并使用无主引用将对notify函数的调用注入到其闭包中:
class ViewHandler {

    private let viewController2 = ViewController2()

    func getViewController() -> ViewController2 {
        viewController2.onDidLoad = { [unowned self] in
            self.notify()
        }
        return viewController2
    }

    func notify() {
        print("My viewcontroller has loaded its view!")
    }
}

然后,当其视图控制器由另一个视图控制器呈现时,ViewHandler将在nill输出时泄漏:
class ViewController: UIViewController {

    private var viewHandler: ViewHandler?

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        viewHandler = ViewHandler()
        self.present(viewHandler!.getViewController(), animated: true, completion: nil)

        viewHandler = nil // ViewHandler is leaking here.
    }
}

我知道这个例子看起来有点做作,但据我所知不应该有漏洞。让我试着打破它:
在呈现ViewHandler.ViewController2之前,所有权应如下所示:
ViewController -> ViewHandler -> ViewController2 -|
                       ^                          |
                       |_ _ _ _ unowned _ _ _ _ _ |

在呈现ViewHandler.ViewController2之后,所有权应该如下所示:
         _______________________________
        |                               v
ViewController -> ViewHandler -> ViewController2 -|
                       ^                          |
                       |_ _ _ _ unowned _ _ _ _ _ |

删除ViewHandler之后,所有权应该如下所示:
         _______________________________
        |                               v
ViewController    ViewHandler -> ViewController2 -|
                       ^                          |
                       |_ _ _ _ unowned _ _ _ _ _ |

没有任何东西拥有ViewHandler,应该释放它。但是情况并非如此,ViewHandler正在泄漏。
如果我将注入onDidLoad的闭包的捕获列表中的引用更改为弱引用,则不会出现泄漏,ViewHandler将按预期释放:
func getViewController() -> ViewController2 {
    viewController2.onDidLoad = { [weak self] in
        self?.notify()
    }
    return viewController2
}

另外,我无法解释的是,如果我保持引用为无主,并使ViewHandler从NSObject继承,那么ViewHandler将按预期释放,并且不会泄漏:
class ViewHandler: NSObject {

    private let viewController2 = ViewController2()

    func getViewController() -> ViewController2 {
        viewController2.onDidLoad = { [unowned self] in
            self.notify()
        }
        return viewController2
    }

    ....
}

有谁能解释发生了什么事吗?

最佳答案

根据我目前的理解,符合NSObjectProtocol的NSObject。这类对象是从具有成熟内存管理功能的Objective-C桥接而来的。当你使用class时,我们大多数人仍然使用这种类。如果您是从NSObject构建一个类,应该不会有什么影响。
swift class的管理似乎有点实验性,因为人们喜欢尽可能使用structure。所以有一些意想不到的行为并不奇怪。
因此,当你选择swift class时,你必须根据这一经验进行更多的思考。但是好的一面是它们可能带来一些新的和稳定的特性,这与经典的NSObject不同。
为了简单起见,只需将vc2作为私有变量删除。

class ViewHandler {

func getViewController() -> ViewController2 {

    let viewController2  = ViewController2()

    viewController2.onDidLoad = { [unowned self] in
        self.notify()
    }
    return viewController2
}

func notify() {
    print("My viewcontroller has loaded its view!")
}


}

在这种情况下,泄漏仍然存在。用unowned来判断是很困难的。实际上,viewHandler的所有权已经转移到了vc2。
当vc2释放时,泄漏也消失了。这是暂时的泄漏。
  var firstTime: Bool = true

    override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

      if firstTime{
        viewHandler = ViewHandler()
        let vc = viewHandler!.getViewController()
        self.present(vc, animated: true, completion: nil)

        viewHandler = nil // ViewHandler is leaking here.

           DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
              vc.dismiss(animated: true, completion: nil)
               // leaking is over.
           }
    }
    firstTime.toggle()
}

甚至具体来说,所有权也由vc.onDidLoad占有。如果
     viewHandler = nil // ViewHandler is leaking here.

任何一个
     vc.onDidLoad?() // error "ViewHandler has been dealloc!!"


     vc.onDidLoad = nil. // There is no error here.

所以你应该在这里处理。从而解决了“渗漏”问题。

关于swift - 呈现 View “拥有”时,Swift无主自我泄漏,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55264451/

10-10 17:36