【问题标题】:Property Initialization with closures带闭包的属性初始化
【发布时间】:2018-03-07 20:55:59
【问题描述】:

我正在研究 ARC 和强引用循环并遇到了我的以下代码:

class TestClass: UIView {
  let button: UIButton = {
    let view = UIButton()
    view.frame = CGRect(x: 50, y: 50, width: 200, height: 200)
    view.backgroundColor = .blue
    view.translatesAutoresizingMaskIntoConstraints = false
    view.setTitle("Button", for: .normal)
    view.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
    return view
  }()

  @objc private func buttonClicked() {
    print("Clicked")
  }

  override init(frame: CGRect) {
    super.init(frame: frame)
    print("Object of TestClass initialized")
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  deinit {
    print("Object of TestClass deinitialized")
  }
}

在闭包内的addTarget 方法中对self 的引用似乎不会创建强引用循环。

谁能解释一下原因?

另外,我注意到如果我从UIView 中删除继承,编译器就会开始抱怨:Use of unresolved identifier 'self'。

有人也可以解释一下吗,为什么在这种情况下会发生而在第一个情况下却没有?

【问题讨论】:

  • 表示视图还没有被加载。您可以使用 lazy 关键字声明您的变量。 lazy var button: UIButton = {
  • 另一种选择是在didMoveToSuperview方法中初始化你的按钮
  • @LeoDabus,我明白了。但为什么它会在 XCode 项目(单页应用程序)而不是 Playground 文件中工作?

标签: swift closures strong-references


【解决方案1】:

这不是保留周期,因为self 不是您认为的那样:)

具有初始值的属性甚至在任何初始化程序运行之前都会“执行”,并且对于这些属性self 指向此类型的更高阶函数:

(TestClass) -> () -> TestClass

因此,您并没有真正访问实例,而是访问类似static 的方法,该方法对所有具有默认值的属性进行初始化。这就是您没有保留周期的原因。

addTarget 的第一个参数接受 Any? 值,因此这不违反任何类型规则,因此编译器不会抱怨您没有在那里传递 NSObject 实例。

检查后来的行为 - 例如如果将按钮添加到 UI 层次结构并被点击会发生什么,这揭示了一些有趣的事情:运行时看到您将非对象作为目标传递并为目标和操作设置 null 值:

【讨论】:

  • 谢谢 Cristik,我需要一些时间来消化你答案的第一部分,但关于第二部分。 print("Clicked") 得到执行。不是这样吗,因为就像你说的那样,目标和行动都设置为空?
  • @Alexander.Kazakov 从您发布的代码中应该是不可能的,原因很简单,您没有在 UI 层次结构中添加按钮
  • 假设按钮已添加到 ui 层次结构中。为什么会起作用?
  • @Alexander.Kazakov 使用问题中的代码并在 UI 层次结构中添加按钮会导致答案中描述的行为。也许你在你的应用中做一些不同的事情。
猜你喜欢
  • 2019-01-25
  • 2020-10-26
  • 2012-12-15
  • 2018-09-09
  • 2017-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多