Swift 中的自动引用计数

问题描述 投票:0回答:1

我对 Swift 中的 ARC 有疑问,请看下面的代码。

当出现

SecondViewController
时,将弹出警报,单击“确定”按钮后,
SecondViewController
将解散。这是我的问题,在我看来,
SecondViewController
保留
UIAlertController
UIAlertController
保留
UIAlertAction
,以及
UIAlertAction
调用
back()
中的函数
SecondViewController
。所以在这种情况下它可能会导致保留周期,并且根据视频here(大约8:30)它确实会导致保留周期。但是,当
SecondViewController
关闭时,调用
deinit
函数,它会打印
SecondViewController is being deinitializing
。所以我很困惑哪个是正确的。

class SecondViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .orange
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let alert = UIAlertController(title: "Hi", message: "everyone", preferredStyle: .alert)
        let ok = UIAlertAction(title: "OK", style: .default) { _ in
            self.back()
        }

        alert.addAction(ok)
        self.present(alert, animated: true)
    }

    private func back() {
        dismiss(animated: true)
    }

    deinit {
        print("SecondViewController is being deinitializing")
    }

}

这是另一个例子。我在 playground 中运行以下代码,当我将

deinit
设置为 nil 时,
test
函数仍然被调用。我不明白为什么在
[weak self]
函数中的
value
之前不添加
hehe()
就调用它。

class Test {

    var int: Int = 0

    func hehe() {
        fetchAPI(input: 3) { value in
            self.int = value
        }
    }

    func fetchAPI(input: Int, completion: @escaping (Int) -> Void) {
        completion(input)
    }

    deinit {
        print("Test in being deinitializing")
    }

}

var test: Test? = Test()

test?.hehe()

test = nil

我看过 Swift 文档,但找不到类似的例子。

我希望在我添加 [weak self] 之前不会调用

deinit
函数。我知道 ARC 不适用于值类型,但仍然不确定。有没有人可以对此进行更多解释?谢谢。

[更新]

这里是第三个例子,会造成retain cycle。我猜是因为

UIAlertController
UIAlertAction
互相保留,而
UIAlertAction
也保留了
SecondViewController
。所以当
UIAlertController
dismiss 时,
UIAlertAction
中的闭包仍然存在,这会导致保留循环。我说得对吗?

class SecondViewController: UIViewController {
    
    var array: [String] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .orange
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let alertController = UIAlertController.init(title: "Test", message: nil, preferredStyle: .alert)
        alertController.addTextField(configurationHandler: nil)
        let alertAction = UIAlertAction.init(title: "OK", style: .default) { (action) in
            let text = alertController.textFields![0].text
            self.array.append(text!)
        }
        alertController.addAction(alertAction)
        self.present(alertController, animated: true, completion: nil)
    }

    deinit {
        print("SecondViewController is being deinitializing")
    }

}
swift automatic-ref-counting
1个回答
3
投票

首先,检查 Playground 中的内存管理是没有意义的。游乐场以非常不寻常的方式运作。您无法在 Playground 中探索与内存、性能、线程或系统交互相关的任何内容。它们纯粹是为了玩弄算法和数据结构。

就是说,您描述的第一种情况是预料之中的。 SecondViewController 仅在 UIAlertController 出现时保留它(当它被关闭时,它被释放)。 UIAlertAction 仅在调用它之前保留其回调。之后它被释放。这是一种常见的模式,即抓住你需要的对象,直到你不再需要它们,然后释放它们。这确保对象在其生命周期内存在,然后可以被销毁。

您的测试示例非常相似,但更简单。

fetchAPI
返回后立即释放闭包,因为它从未分配给任何东西。你标记了它
@escaping
,但它实际上并没有逃脱。

© www.soinside.com 2019 - 2024. All rights reserved.