0

我有UIViewController子类。我有一个我想延迟加载的常量。为此,我使用了一个函数来设置它的默认值。我在这里有两种init方法,因为我以前使用它们来设置值。然后我发现我不能调用一个常用的方法来设置值。似乎只有 init 方法可以设置类常量的值。当我创建函数来设置默认值时,我发现我有一个无限循环,它从 init(nibName, bundle) 函数中重复调用我的函数作为目标。我是 swift 新手,不知道我做错了什么。

class ViewController: UIViewController {

  let goal: Goal =
  {
    return Goal()
  }()

  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)
  {
    super.init()
  }

  required init(coder decoder: NSCoder)
  {
    super.init()
  }
}
4

1 回答 1

1

当你重写一个方法时,你应该调用这个方法的超级实现。

required override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
  super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}

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

let量值只能在初始化器中设置。如果您有超过 1 个初始化程序,请考虑如何链接它们。拥有 1 个指定的初始化程序和许多便利非常有用。通过这种方式,您只能将变量设置在 1 个位置。例子

class A {

  let age: Int
  let name: String
  let type: String

  init(age: Int, name: String, type: String) {
    self.age = age
    self.type = type
    self.name = name
  }

  convenience init() {
    self.init(age: 0, name: "", type: "")
  }
  convenience init(age: Int) {
    self.init(age: age, name: "", type: "")
  }
  convenience init(age: Int, name: String) {
    self.init(age: age, name: name, type: "")
  }

在 swift 中,您必须在 init 方法中完全初始化类。这意味着您必须为所有属性设置值并调用 super init(如果存在)。之后,您可以访问 self 和 call 方法。

在 Cocoa 框架中,类有超过 1 种方法来实例化它们并有许多初始化程序。示例 UIViewController 具有
init(nibName: String?, bundle: NSBundle?)以及init(coder :NSCoder) 我们将如何初始化此类?

  1. 设置变量值

这个解决方案有一个巨大的缺点 - 它不是DRY
你在 2 个地方有相同的代码。如果您需要更改或修复它,您需要在 2 个地方执行相同的操作。非常糟糕的架构和代码气味。

var name: String

  required init(coder aDecoder: NSCoder) {
    name = "Name"
    super.init(coder: aDecoder)    
  }

  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    name = "Name"
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
  }

我们能做得更好吗?解决方案看起来很明显 - 在单独的方法中提取名称初始化。但我们不能这样做,因为Swift 类安全规则
Initialization Steps

  1. 设置所有属性
  2. 调用 super.init(...)
  3. 现在您的类和超类都已完全初始化。只有在这里您可以访问 self 并调用 self 上的方法

代码:

 required init(coder aDecoder: NSCoder) {
    setDefaultName() // Error. Can't access self yet
    super.init(coder: aDecoder)
  }

 func setDefaultName() {
    name = "Name"
  }
  1. 默认值

设置变量的默认值。
这看起来更好,因为我们只在一个地方初始化属性。

 var name: String = ""

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

  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
  }

如果你需要更复杂的逻辑来初始化你的变量,你可以使用闭包。
我不喜欢这样,因为现在你的类变量声明中有逻辑(与函数相同的闭包)。
我们能做得更好吗?我们可以!

 var name: String = {
    var name = "Name"
    // ....
    // Other complicated logic
    name + " Is Best"
    return name
  }()
  1. 工厂方法

最好把这个闭包移到类变量定义之外,
这样我们就可以做类似的事情var name = Factory.defaultName()

Swift 支持内部类。您可以在类中创建一个类。通过这种方式,您可以将一些功能分组到一个类中。这听起来很适合将初始化方法分组到工厂类
最终示例中:

var name: String = Factory.defaultName()

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

  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
  }


  class Factory {
    class func defaultName () -> String {
      return "Name"
    }
  }
于 2014-10-19T15:13:06.130 回答