【问题标题】:How does closures capture data?闭包如何捕获数据?
【发布时间】:2017-06-23 01:29:33
【问题描述】:

我不明白闭包捕获数据的概念。有人可以使用闭包编写示例代码来显示数据如何永远不会被破坏。我已经阅读了Apple文档,但我仍然感到困惑。以及“无主”和“弱”在闭包中有何不同...

class TableViewController: UITableViewController {

var allWords = [String]()
var usedWords = [String]()


override func viewDidLoad() {
    super.viewDidLoad()

    if let allWordsPath = Bundle.main.path(forResource: "start", ofType: "txt"){

        if let startWords = try? String(contentsOfFile: allWordsPath){

            allWords = startWords.components(separatedBy: "\n")

        }else{

            allWords = ["Cake"]
        }

        startGame()
    }

    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Make Word", style: .plain, target: self, action: #selector (makeWord))
}

func startGame(){

    allWords = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: allWords) as! [String]
    title = allWords[0]
    usedWords.removeAll(keepingCapacity: true)

}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return usedWords.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Word", for: indexPath)
    cell.textLabel?.text = usedWords[indexPath.row]
    return cell

}


func makeWord() {

    let ac = UIAlertController(title: "Add Word", message: nil, preferredStyle: .alert)
    ac.addTextField(configurationHandler: nil)


    let submit = UIAlertAction(title: "Submit", style: .default){ [unowned self,ac]

        (action: UIAlertAction!) in
        let answer = ac.textFields?[0]
        self.submit(answer: (answer?.text)!)

        }

    ac.addAction(submit)
    present(ac,animated: true)
}


var number = 10
func submit(answer: String){
    usedWords.append(answer)
    tableView.reloadData()

}

如果我们没有明确地释放事物,那么 unowned 在这里如何工作..

【问题讨论】:

  • 我看不懂他的例子。
  • 检查我的答案。顺便说一句,闭包并不能确保您的数据永远不会被破坏。它们只是允许您指定应如何处理数据,以及在数据被破坏时该怎么做。将变量设置为“弱”然后简单地在闭包内检查它是完全有效的。

标签: swift


【解决方案1】:

您应该首先搜索强、弱和无主之间的区别。这里有很多关于它的答案。

无论如何,在这种特定情况下:

你的闭包有这个代码:

[unowned self,ac]

这称为“捕获列表”。它表示在创建 BLOCK 时应该通过值“捕获”的东西。 (如果你没有在这里指定它们,并且你在块之后的某个地方改变了值,那么块内的值也会被改变)。

self 是 unowned 并且不需要被释放的原因是因为 unowned 的意思是:

“不要担心这个变量的内存管理,它总是会 在我的关闭期间有一个值”

所以,回到 unowned self,你应该从闭包中声明 self 变量是 weak 或 unowned 的原因是,如果不是,你会创建一个保留循环。只要有东西引用它们,就不能解除分配。所以在这种情况下,你的 TableViewController 让你的闭包保持活跃,你的闭包让你的 TableViewController 保持活跃。因此,因为它们相互引用,所以它们中的任何一个都无法正确释放。 -> 内存泄漏

因此我们可以得出结论,self 要么是弱的,要么是无主的。对于本示例中的所有意图和目的,它们完全相同。它们都通过消除闭包保持自身存活的能力来达到“打破保留循环”的目的。那么你问谁会释放自我?你的关闭不在乎。但是在你的关闭之外思考。您的 TableViewController 正在调用您的闭包,因此由于这里没有发生奇怪的事情,我们可以安全地假设,如果显示警报,它必须明确显示在您的 TableViewController 上。因此,一旦您关闭警报或其他任何内容,您的 TableViewController 将继续照常工作。一个你解雇你的 TableViewController 虽然, self 将被释放(因为闭包将它引用为无主),但此时没有任何方法显示警报。但是,如果您做了一些奇怪的事情,使您的 TableViewController 在仍然显示警报时被解除,那么一旦用户“提交”您的应用程序就会崩溃。因为通过声明您的变量无主,您基本上向您的闭包做出了一个承诺,即它不必担心 self 实体,因为只要您的闭包还活着,它就会一直存在。

【讨论】:

  • 那么基本上如果我们不放unowned或者weak,闭包里面的属性会受到外面变化的影响吗?还是它们只需要用于解除分配的目的...另外,viewController 的所有属性都引用了自身,所以它们如何解除分配。
  • 你在混合东西。 Unowned 或 Weak 与是否在外部修改无关。好的,首先,括号 [ properties ] 内的内容不会受到外面发生的事情的影响。其次,weak and unowned 是释放目的所必需的。为了避免保留循环。第三,viewController 中的所有属性都由 viewController 本身保持活动状态,即“Self”。当视图控制器被解除分配时,它们被解除分配,当您关闭它时会发生这种情况。这与闭包无关,您只需要确保仅在 VC 处于活动状态时使用闭包即可。
  • 哦,好吧,最后一件事:内存泄漏有什么不好。我可以忽略无主或弱者,然后离开它。
  • 通过向闭包传递强引用会导致内存泄漏,而这个传递的东西恰好也使闭包保持活动状态。如果您忽略内存泄漏,您的应用程序最终将耗尽内存。小而简单的类并不那么明显。但是,例如,如果您泄漏了“图像”对象或其他资源繁重的东西,那么系统将无法恢复此内存并最终终止您的应用程序。
  • 哦,好吧。编译器是否有一种简单的方法来检测内存泄漏,以便我们可以返回并添加弱/无主?
【解决方案2】:

看看这个。我正在创建 2 个相同类型的对象。一个对保留自身的闭包的引用,因此即使创建它的函数超出范围,对象和闭包也会相互保留并且永远不会被释放。第二个对象的闭包对该对象有一个弱引用,因此当对象创建函数超出范围时,引用计数为 0,当它被释放时,它也会释放闭包。

import UIKit
import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

class B {

    deinit {
        print("\(name) deinit")
    }

    var name: String

    init(name: String) {
        self.name = name
    }
    var zort: (() -> ())?

    func someMethod() {
        print("")
    }
}

func createStuffThatNeverGoesAway() {

    var b: B = B(name: "bad");
    b.zort = {
        b.someMethod()
    }
}

func createStuffThatGoesAway() {

    var b: B = B(name: "good");
    b.zort = { [weak b] in
        b?.someMethod()
    }
}

createStuffThatNeverGoesAway()
createStuffThatGoesAway()

输出:

good deinit

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多