可选结构的非可选 属性 显示为可选

Non-optional property of optional struct being shown as optional

城镇结构:

struct Town {
    var population: Int = 5_422
    var numberOfStoplights: Int = 4

    func printDescription() {
        print("The town has \(population) people and \(numberOfStoplights) stoplights")
    }

    mutating func changePopulation(by amount: Int) {
        population += amount
    }
}

怪物class:

class Monster {
    var town: Town?
    var name = "Monster"

    func terrorizeTown() {
        if town != nil {
            print("\(name) is terrorizing a town")
        } else {
            print("\(name) hasn't found a town to terrorize yet")
        }
    }
}

僵尸class:

class Zombie: Monster {
    var walksWithLimp = true

    final override func terrorizeTown() {

        if town?.population > 0 {
            town?.changePopulation(by: -10)
        }

        super.terrorizeTown()
    }
}

我在 town?.population 上遇到编译器错误 'value of optional Int not unwrapped...'。我还没有声明 population 是可选的,如果我将其更改为 town?.population? 它表示 population 是一个非可选的 Int。这两个错误相互矛盾,这是怎么回事?

表达式 returns 是可选的 Int 但您仍在使用非可选语法测试它。您需要使用 if let.

if let pop = town?.population {
    if pop > 0

等等

可选链不删除可选性。它只是将其级联,以便链中的任何 nil 都会导致 nil 在末尾弹出。但结果仍然是可选的。

因为 town?.population 还是 Int?, 你不能比较 Int?用 Int 除非你从 Int 得到 Int?

所以代码可以是

if let town = town, town.population > 0 {
    town.changePopulation(by: -10)
}
super.terrorizeTown()

你必须初始化你的城镇结构实例,否则它总是return nil。所以我看到你的代码了,Town?总是 return 零。

可选链接,即使在查询非可选方法和属性时,也会产生一个可选的结果

考虑以下示例:

struct Foo {
    let bar: Int // not: not an optional
    init(_ bar: Int) { self.bar = bar }
}

let foo: Foo? = Foo(42) // 'foo' is an optional

如果我们不是特别对 foo 感兴趣,而是特别对其成员 bar 感兴趣,我们可以使用 optional chaining 查询对 bar 的访问 foo,如果 foo 不是 nil,这将为我们提供包含在 .some(...) 中的 bar 的值,否则为 nil。也就是说,这样的查询(即使是非可选的 属性 as `bar)也会产生一个可选的:

let optBar = foo?.bar // this is now an optional (Optional<Int>)

To reflect the fact that optional chaining can be called on a nil value, the result of an optional chaining call is always an optional value, even if the property, method, or subscript you are querying returns a nonoptional value.

在我们继续之前,我们还注意到在 Swift 3 中我们可能不再比较 Comparable 运算符的可选操作数,如以下接受和实施的 Swift 演化所述建议:

因此,如果您想有条件地进入 if 块,请给出以下条件:

  • foo 不是 nil,并且
  • 如果是,bar 大于 0

然后您首先需要以某种方式实现(可能存在的)bar 的具体值,然后再将其与 0 进行比较。

您可以这样做,例如组合的可选绑定和布尔条件 if 语句:

if let nonOptionalBar = foo?.bar, nonOptionalBar > 0 {
    print("'foo' is not nil and its 'bar' member is larger than 0")
} // prints "'foo' is not nil and its 'bar' member is larger than 0"

如果您不打算使用值 bar,但是(并且只想减少它的数量),您可以使用 nil coalescing operator 来构造条件 if 语句将失败,因为 foonil 或其成员 bar 不大于 0:

if (foo?.bar ?? -1) > 0 {
    print("'foo' is not nil and its 'bar' member is larger than 0")
}

在这里,如果 foonil(foo?.bar ?? -1) 将导致 -1(随后将导致 false for -1 > 0) ,或者在 foo 不是 nil.

的情况下导致 bar 的值(作为非可选的具体 Int

我们也可以利用 map method of Optional 有条件地(即 foo 不是 nil)测试比较,如果 foonil,在调用 map.

的 return 上使用 nil 合并运算符提供 false 作为默认值
if foo.map({ [=14=].bar > 0 }) ?? false {
    print("'foo' is not nil and its 'bar' member is larger than 0")
}

适用于您的示例的三个备选方案:

if let population = town?.population, population > 0 {
    town?.changePopulation(by: -10)
}

if (town?.population ?? -1) > 0 {
    town?.changePopulation(by: -10)
}

if town.map({ [=15=].population > 0 }) ?? false {
    town?.changePopulation(by: -10)
}