【问题标题】:Accessing "self" in initializing closure在初始化闭包时访问“self”
【发布时间】:2017-01-26 04:02:49
【问题描述】:

在 Swift 3 中,dispatch_once 函数被移除,migration guide 建议使用初始化闭包:

让 myGlobal = { ... global 包含对闭包的调用中的初始化 ... }()

_ = myGlobal // 使用 myGlobal 只会在第一次使用时调用初始化代码。

我想从初始化闭包中访问“self”实例变量,如下所示:

class SomeClass {
    var other = SomeOtherClass()
    
    let initialize: () = {
        // self.other - this doesn't work, complains about unresolved identifier 'self'
        // how to access self.other here?
    } ()

    func doSomething() {
        // initialize will only be called once
        initialize
    }
}

为什么在闭包中无法访问“self”,如何才能访问?

【问题讨论】:

  • 将 dispatch_once 与实例属性一起使用以确保“每个实例一次”初始化总是错误的,例如,请参阅 Apple 工程师的回答 stackoverflow.com/a/19845164/1187415
  • 那么在 Swift 3 中确保实例初始化代码只运行一次并且能够设置实例变量的正确方法是什么?在这种情况下,我不能使用 init() 的变体,因为类 (NSViewController) 只需要运行一次初始化代码,但在 NSViewController 生命周期中的某个点之后(即 viewDidAppear)。您认为@vadian 提供的答案是否正确(似乎工作正常)?

标签: swift swift3


【解决方案1】:

引用的迁移指南示例具有误导性,因为它与全局变量相关。

instance let 常量的闭包在类初始化时立即被调用(一次)。这就是为什么它不能使用在同一级别声明的其他变量的原因。

你可以做的是懒惰地初始化initialize(变量名不是最好的;-))。闭包也只被调用一次,但是——正如指南所描述的——只有第一次(何时)使用它

class SomeClass {
  let other = SomeOtherClass()

  lazy var initialize : () = {
    let test = self.other
    test.doSomething()
  }()

  func doSomething() {
    // initialize will only be called once
    _ = initialize
  }
}

【讨论】:

  • 您认为 Apple 的迁移指南有什么问题?它告诉您可以使用“延迟初始化的全局变量或静态属性” 而不是 dispatch_once。问题中的 instance property 既不是延迟初始化的全局属性,也不是静态属性。 – 为此目的使用 dispatch_once 总是错误的,因此“迁移”不适用。另请参阅我对问题的评论。
  • @MartinR 这个特殊的语法是错误的。 _ = myGlobal 行表明 let myGlobal 闭包是延迟初始化的。实际上,get 变量的这一行根本不需要。
  • 迁移指南是关于一个全局变量 myGlobal 来执行一些代码每个应用程序运行时一次,它不能在这里应用。就我所见,迁移指南中没有任何问题。
  • @MartinR 你是对的,但如果脱离上下文,它就会产生误导。我编辑了答案以进行澄清。
【解决方案2】:

当“SomeClass”类的一个实例被创建时,它将首先在该实例上创建所有变量和常量。在此期间,self 可能没有完全初始化,因为它可能是在设置的一半。因此,在初始化步骤完成之前,self 不可用。

在示例中,他们谈论的是一个没有自我概念的全局变量,或者是一个同样没有自我概念的类上的静态常量。

如果它需要是实例方法/变量,您可以:

a) 让它像一个惰性变量

lazy var initialise : ()->Void = { 
    return {
        // can access self here
    }    
}()

它将在您第一次调用时创建,而不是在初始化期间创建。当然,这样会丢失常量,并且必须存储闭包,这很浪费,因为您只执行一次。

b) 将代码放入 init 方法中:

init() {
    // if your class doesn't have a super class, you can access self.other here. 
    // If it does have a super class (like NSObject) you must first call super.init() here to complete the initialisation. 
    // This can only be done after all other variables have been set.
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-15
    • 1970-01-01
    相关资源
    最近更新 更多