【问题标题】:Pass self as argument within init method in Swift 1.2在 Swift 1.2 的 init 方法中将 self 作为参数传递
【发布时间】:2015-06-20 07:49:48
【问题描述】:

以下类具有声明为隐式展开变量的“let”属性。这以前适用于 Xcode 6.2:

class SubView: UIView {

    let pandGestureRecognizer: UIPanGestureRecognizer!

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.pandGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panAction:")
    }

    func panAction(gesture: UIPanGestureRecognizer) {
        // ...
    }
}

更新到 Xcode 6.3(使用 Swift 1.2)后,出现以下编译错误:

Property 'self.panGestureRecognizer' not initialized at super.init call
Immutable value 'self.panGestureRecognizer' may only be initialized once

super.init 调用之前移动以下行:

self.pandGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panAction:")

给出以下错误:

'self' is used before super.init call

属性“panGestureRecognizer”不需要突变,因此必须将其声明为常量“let”。由于它是一个常量,因此它必须在声明时具有初始值,或者在init 方法中对其进行初始化。要初始化它,它需要在'target'参数中传递'self'。

其他线程建议将其声明为隐式解包可选,并在super.init 调用之后对其进行初始化。这在我更新到 Xcode 6.3 之前一直有效。

有人知道这种情况的正确实现或解决方法吗?

【问题讨论】:

  • 这看起来像是该语言的实现,甚至是规范中的一个错误。向 Apple 提交错误报告是个好主意。不幸的是,当您注册对一种恰好相对复杂的全新编程语言进行 beta 测试时,您就会得到这样的结果。
  • 它不必声明为常量 let。实际上,如果您要在文件顶部将其声明为可选项,则需要将其声明为 var,以便在 init 方法中对其进行实例化。
  • @rdelmar 你可以声明 let 变量,它只是强制它们在 super.init 之前被实例化。

标签: ios xcode swift


【解决方案1】:

问题

问题是您使用了let - 声明为let 的选项没有被赋予nil 的默认值(但是var 是)。 Swift 1.2 中引入的以下内容将无效,否则您将无法在声明 myOptional 后为其赋值:

let myOptional: Int?

if myCondition {
    myOptional = 1
} else {
    myOptional = nil
}

因此,您会收到错误“Property 'self.panGestureRecognizer' not initialized at super.init call”,因为在调用 super.init(coder: aDecoder) 之前,因为 panGestureRecognizer 不是 nil;它根本没有被初始化。

解决方案:

1.panGestureRecognizer 声明为var,这意味着它将被赋予默认值nil,然后您可以在调用super.init(coder: aDecoder) 后更改该值。

2. 在我看来,更好的解决方案是:不要使用隐式展开的可选项,并声明 panGestureRecognizer 的初始值为 UIPanGestureRecognizer()。然后在super.init被调用后设置目标:

class SubView: UIView {
    let panGestureRecognizer = UIPanGestureRecognizer()

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        panGestureRecognizer.addTarget(self, action: Selector("panAction:"))
    }
}

【讨论】:

  • 我喜欢你的第二个解决方案。它适用于这种情况,您可以在代码的后面部分。
  • 我喜欢并且更喜欢您的第二种解决方案。它适用于这种情况,您可以在代码的后面部分设置目标。但是,对于其他一些情况,可能没有机会这样做。例如,让我们将panGestureRecognizer 替换为UIDynamicAnimator。在这种情况下,只能在创建实例时设置参考视图(例如self.animator = UIDynamicAnimator(referenceView: self)
  • 这很好。在这种情况下,您可能必须使用一个变量,隐式展开可选并在super.init 之后设置它的值。或者,您可以使用 lazy 变量 - 看看 Robert Vojta 的回答。另外,如果您觉得这个或任何答案解决了您的问题,您是否可以考虑将其中一个标记为正确。
【解决方案2】:

针对这种特定情况的解决方法是:

class SubView: UIView {

let pandGestureRecognizer: UIPanGestureRecognizer

required init(coder aDecoder: NSCoder) {
    self.pandGestureRecognizer = UIPanGestureRecognizer()
    super.init(coder: aDecoder)
    self.pandGestureRecognizer.addTarget(self, action: "panAction:")
}

func panAction(gesture: UIPanGestureRecognizer) {
    // ...
}

}

【讨论】:

  • 是的,这就是我要说的。 let 变量不应阻止您在实例化后添加目标。
  • 在实例化后使用 let 不会阻止您添加目标?
  • 没错,使用 let 并不会阻止你在实例化后添加目标。因为UIPanGestureRecognizer 是一个可变实例。
【解决方案3】:

您不能使用self,除非该类已初始化。如果您想使用self 进行属性初始化,则必须是lazy。但是let不支持lazy,只支持var

那是因为:

您必须始终将惰性属性声明为变量(使用 var 关键字),因为它的初始值可能直到之后才被检索到 实例初始化完成。常量属性必须始终 在初始化完成之前有一个值,因此不能 声明为惰性。

这是一种妥协,如果你可以和私人二传手一起生活,你可以这样做:

class SubView: UIView {

  private(set) lazy var panGestureRecognizer: UIPanGestureRecognizer = { [unowned self] in UIPanGestureRecognizer(target: self, action: "panAction:") }()

  required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }

  func panAction(gesture: UIPanGestureRecognizer) {
  }
}

或仅使用 UIPanGestureRecognizer() 初始化 panGestureRecognizer 并稍后添加目标。

【讨论】:

    【解决方案4】:

    如果您想将 self 传递给对象的初始化程序,您应该将您的对象声明为惰性对象。因为当这个对象初始化的时候,self还没有准备好。

    lazy var viewModel = IntroViewModel(controller: self)
    
    class IntroViewModel {
    
        private weak var controller: IntroViewController?
    
        init(controller: IntroViewController?) {
            self.controller = controller
        }
    }
    

    【讨论】:

      【解决方案5】:

      我遇到这个问题的原因不同,它与 Optionals 或懒惰无关。从字面上看,person 对象必须初始化一次。

      class Person {
          var name: String
          init(name: String) {
              self.name = name
          }
      }
      
      class Account {
          static let shared = Account(person: Person(name: "Bobby")) // <-- person initialized once
          let person: Person = Person(name: "Lio") // initialized again!
      
          init(person: Person) {
              self.person = person
          }
      }
      

      Swift 可以捕捉到这个错误,这很有趣

      【讨论】:

        猜你喜欢
        • 2021-10-20
        • 1970-01-01
        • 1970-01-01
        • 2012-02-07
        • 1970-01-01
        • 2022-01-16
        • 1970-01-01
        • 2022-01-05
        • 1970-01-01
        相关资源
        最近更新 更多