如何从 swift 3 中的 init 尽早 return

How to return early from an init in swift 3

我有一个应用程序,我想在其中根据是否已经有一个等效的对象保存到用户默认值来创建一个对象。如果检测到对象,我想尽早在 class init 和 return 中检测到这一点。这就是我想要做的:

init() {

    /* There are two possibilities when creating a hero:
     1. The hero is brand new and needs to be built from scratch
     2. The hero is loaded from defaults  */

    // Check to see if there is existing game data:

    if defaultExistsForGameData() {
        // This means there is a hero to load and no need to create a new one
        self = extractHeroFromDefaults() // This just loads from UserDefaults
        print("Loading hero from defaults with name of: \(hero.heroName).")

        return self
    }

    // These actions are for creating a brand new hero
    let size = CGSize(width: 32, height: 32)
    let heroTexture = SKTexture(imageNamed: "hero2.ico")
    super.init(texture: heroTexture, color: .clear, size: size)

    self.isUserInteractionEnabled = true
    self.name = "hero"

    self.zPosition = 50

}

控制台中有几个错误,self 是不可变的,等等。我想知道这是否是一个有效的模式,或者我是否应该采取完全不同的方法。

在 Swift 中(与 ObjC 不同),init 不能 return 与自身不同的对象。实现您在这里尝试做的事情的一种常见方法是使用 class 工厂方法(如果您不希望其他对象能够直接调用它,可以选择将 init 设为私有)。

例如,类似以下内容:

class func loadOrCreate() -> Hero {
    if defaultExistsForGameData() {
        // This means there is a hero to load and no need to create a new one
        print("Loading hero from defaults with name of: \(hero.heroName).")
        return extractHeroFromDefaults() // This just loads from UserDefaults
    } else {
        return Hero()
    }
}

private init() {
    let size = CGSize(width: 32, height: 32)
    let heroTexture = SKTexture(imageNamed: "hero2.ico")
    super.init(texture: heroTexture, color: .clear, size: size)

    self.isUserInteractionEnabled = true
    self.name = "hero"

    self.zPosition = 50
}

另一种更接近您当前 API 的方法是创建一个单独的(可能是私有的)指定初始化程序,如下所示:

private init(name: String, zPosition: Int) {
    let size = CGSize(width: 32, height: 32)
    let heroTexture = SKTexture(imageNamed: "hero2.ico")
    super.init(texture: heroTexture, color: .clear, size: size)

    self.isUserInteractionEnabled = true
    self.name = name
    self.zPosition = zPosition
}

public convenience init() {
    let name: String
    let zPosition: Int
    if defaultExistsForGameData() {
        name = defaultName() // Read it out of user defaults
        zPosition = defaultZPosition
    } else {
        name = "hero"
        zPosition = 50
    }
    self.init(name: name, zPosition: zPosition)
}

这种方法的一个问题是它可能有点令人惊讶。如果您创建多个 Hero 对象,目前还不清楚会发生什么。 loadOrCreate() 之类的东西非常清楚地表明存在外部影响。