带默认常量值的Swift init无限循环

带默认常量值的Swift init无限循环,swift,uiviewcontroller,class-constants,Swift,Uiviewcontroller,Class Constants,我有UIViewController子类。我有一个常数,我想加载它。为此,我使用了一个函数来设置它的默认值。这里有两个init方法,因为我以前使用它们来设置值。然后我发现我不能调用一个常用的方法来设置值。似乎只有init方法可以设置类常量的值。当我创建函数来设置默认值时,我发现有一个无限循环,它从init(nibName,bundle)函数重复调用我的目标函数。我是斯威夫特的新手,不知道自己做错了什么 class ViewController: UIViewController { let

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

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()
  }
}

重写方法时,应调用此方法的超级实现

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个位置设置变量示例

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框架中,类有不止一种实例化它们的方法,并且有许多初始化器。示例UIViewController具有
init(nibName:String?,bundle:NSBundle?
init(coder:NSCoder)
我们将如何初始化这个类

  • 设置变量值
  • 此解决方案有一个巨大的缺点-它不是
    您有两个相同的代码。如果您需要更改或修复它,您需要在两个位置执行相同的操作。非常糟糕的架构和代码味道

    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)
      }
    
    我们能做得更好吗?解决方案看起来很明显-在单独的方法中提取名称初始化。但是我们不能这样做,因为安全规则
    初始化步骤

  • 设置所有属性
  • 调用super.init(…)
  • 现在,您的类和超级类都已完全初始化。只有在这里,您才能访问self并在self上调用方法
  • 代码:

     required init(coder aDecoder: NSCoder) {
        setDefaultName() // Error. Can't access self yet
        super.init(coder: aDecoder)
      }
    
     func setDefaultName() {
        name = "Name"
      }
    
  • 默认值
  • 设置变量的默认值。
    这看起来更好,因为我们只在一个地方初始化属性

     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
      }()
    
  • 工厂方法
  • 最好将该闭包移到类变量定义之外,
    所以我们可以这样做
    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"
        }
      }
    

    谢谢我没有注意到我没有在每种情况下调用正确的init。当我扩展UIViewController时,是否有一种方法可以为我的子类执行公共初始化工作?我理解您对便利初始化的看法,但我认为在对其他人的实现进行子类化时,我无法做到这一点,因为Xcode和故事板将决定调用哪个初始值设定项。@Brian我已经对我的解决方案添加了更多解释。请看一下它的详细说明。我怀念Objective-C中常见的init方法的使用。如果有一个我们可以表示的方法可以设置公共功能,那就太好了。