关于快速关闭的一个有趣现象

时间:2017-07-19 03:49:07

标签: swift

我在控制器中有一个类对象,然后我在这个对象中有一个闭包。 我将控制器的一个功能分配给对象的闭包,然后页面没有被删除。

我该如何解决这个问题?

import UIKit

class SecondViewController: UIViewController {
    let test = TestObject()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.white
        self.test.select = self.selectButton(index:)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.test.doSomethine()
    }

    func selectButton(index:Int){
        print(index)
    }

    deinit {
        print("deinit")
    }
}
import UIKit

typealias selectBtnBlock = (_ index:Int)->()

class TestObject: NSObject {
    var select:selectBtnBlock?

    func doSomethine(){
        self.select!(1)
    }
}

1 个答案:

答案 0 :(得分:3)

这是因为当您执行以下操作时,test对象的选择关闭强烈会捕获您的SecondViewController

self.test.select = self.selectButton(index:)

我建议您通过Apple的Swift语言参考阅读弱弱类型。您遇到的“有趣现象”称为强参考周期。

基本上,由于Swift使用ARC作为其内存管理模型,因此至少有一个其他对象引用的任何对象都将保持活动状态,并且其内存不会被释放。

在您的情况下,test已通过我提到的行捕获了其父SecondViewContoller。这意味着你有以下情况:

SecondViewController -> (owns) test // since its a member of the class
test -> (strongly captures) SecondViewController // via the assignment

这会在两者之间产生强大的参考周期,并且不允许ARC解除分配。

当它(ARC)尝试释放test时,知道SecondViewController引用它,因此只有在父项也被释放时才能释放它。当它尝试释放SecondViewController时,ARC知道test.select闭包引用了该对象。

由于两者的引用计数都大于1,因此都不会被取消分配。

解决问题的一种方法是写:

self.test.select = { 
    [weak self] // weakly capture self, this prevents a ref cycle 
    (i:Int)->() in // a closure that accepts an Int
    guard let s = self else { return } // check if self is not nil
    s.selectButton(index: i) // finally invoke the required method
}

另一种方式,类似意图:

self.test.select = { [weak self] i in
    self?.selectButton(index: i)
}

此上下文中的weak关键字用于告诉Swift编译器我不想强烈引用我正在捕获的内容(在这种情况下为self)。