Swift init无限循环,默认常量值

时间:2014-10-19 12:40:48

标签: swift uiviewcontroller class-constants

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

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

1 个答案:

答案 0 :(得分: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个位置设置变量。 示例

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(如果存在)。之后,您可以访问自我和呼叫方法。

在Cocoa Framework中,类有多种方法来实例化它们并拥有许多初始化器。示例UIViewController有 init(nibName: String?, bundle: NSBundle?)init(coder :NSCoder) 我们如何初始化这个类?

  1. 设置变量值
  2. 这个解决方案有一个巨大的缺点 - 它不是DRY
    您在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 class安全规则,我们无法做到这一点 初始化步骤

    1. 设置所有属性
    2. 致电super.init(...)
    3. 现在您的班级和超级班级都已完全初始化。只有在这里,您才能访问自我和自我调用方法
    4. 代码:

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

         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. 工厂方法
        2. 最好采用闭包并将其移到类变量定义之外,
          所以我们可以做var name = Factory.defaultName()

          之类的事情

          Swift支持内部类。您可以在类中创建一个类。这样,您可以在类中对某些功能进行分组。这种声音非常适合将初始化方法分组到Factory类中 最后的例子:

          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"
              }
            }