对如何使用变异感到困惑

时间:2015-12-17 02:15:08

标签: arrays swift nsdictionary

我有一个名为Card的简单结构......

struct Card {
    var order: Int? = -1
    var data : [String: String]
    var original : String?

一个名为Deck的集合对象,看起来像......

struct Deck {
    var cards : [Card]

Deck有读写方法,它基本上归结为将从文本文件读入的字符串拆分,然后将其一点一点地推入前一个空data。这是一个例子......

mutating func parseGRCard(var c: Card) {
    c.data["I1"] = c.original![2...4].trim()
    c.data["I2"] = c.original![5...9].trim()
}

要读取文件,我得到每一行,制作一个Card,然后调用它上面的解析方法......

let nc = Card(order: i, original: c)
parseGRCard(nc)
cards.append(nc)

当我单步执行此函数时,我看到mc的original具有预期的数据,即文本文件中的原始行。然后我会看parseGRCard阅读它并将项目添加到data,现在有两项。但是当它返回并附加了nc时,data为空。

我认为mutating应该处理这些事情,但显然我在这里缺少一些基本的东西。

2 个答案:

答案 0 :(得分:1)

我改变了你的代码以使其编译并使其更具说明性。

append正在变异,因为它会改变cards。 它的参数c是一个inout参数,以便在函数之后传递传递的Card。由于Structs是值类型而不是引用类型,因此实际将新副本传递给函数。此行为与作为变异函数的函数无关。

struct Card {
    var placeInDeck: Int = 0
}

struct Deck {
    var cards : [Card] = []

    mutating func append(inout c: Card) {
        c.placeInDeck = cards.count
        cards.append(c)
    }
}

var cardZero = Card()
var cardOne = Card()

var deck = Deck()

deck.append(&cardZero)
deck.append(&cardOne)

cardZero.placeInDeck // 0
cardOne.placeInDeck // 1

在这种情况下,函数不会发生变异,因为Deck的属性没有被更改。 c参数是一个变量,只是在函数范围内使其可变。 注意,这将从Swift中删除。当函数结束时,Card的这个可变副本不会更新它在函数范围之外的原始实例。

Card

答案 1 :(得分:1)

Apple's documentation methods开始,我们可以阅读以下有关mutable关键字的信息:

  

但是,如果您需要修改结构的属性或   在特定方法中枚举,您可以选择进行变异   该方法的行为。然后方法可以变异(即更改)   方法中的 属性,以及它所做的任何更改   当方法结束时,将写回原始结构。

我同意可以将粗体标记的部分解释为"方法的属性,即它的参数?" 。从您上面的示例中,您似乎已经完成了这种解释。

但是mutable关键字只告诉我们允许关联函数更改(mutate)拥有该方法的struct(在本例中)的变量成员值。

struct SingleIntegerValueStruct1 {
    var myInt = 1
    func LetsTryToMutateTheInteger () {
        myInt += 1 // compile time error; myInt immutable
    }
}

然而,如果我们使用mutating关键字

struct SingleIntegerValueStruct {
    var myInt = 0
    mutating func LetsTryToMutateTheInteger () {
        myInt += 1 // ok, mutating function
    }
}

var a = SingleIntegerValueStruct(myInt: 1)
print("\(a.myInt)") // 1
a.LetsTryToMutateTheInteger()
print("\(a.myInt)") // 2

但是,struct类型始终是值类型。因此,当struct类型传递给任何函数时,该函数将无法改变调用者参数,因为它只给出了它的副本。

let nc = Card(order: i, original: c)
parseGRCard(nc)  // passes _copy_ of struct typ nc to parseGRCard
cards.append(nc) // nc in this scope is stil unchanged

如果想要使用可能改变其输入参数的"独立" 函数,输入是结构,您可以,如评论中所述,我们函数参数的inout关键字。

我们从上面扩展我们的例子,包括这样一个案例:

struct SingleIntegerValueStruct {
    var myInt = 0
    mutating func LetsTryToMutateTheInteger () {
        myInt += 1 // ok, mutating function
    }

}

// "b" here is a _copy_ of "a" (in parameter), and the function
// thereafter returns a _copy_ of "b" (with state of "b" as its 
// final state in the function)
func standAloneInOutFunction(inout b: SingleIntegerValueStruct) {
    b.LetsTryToMutateTheInteger()
}

var a = SingleIntegerValueStruct(myInt: 1)
print("\(a.myInt)") // 1
a.LetsTryToMutateTheInteger()
print("\(a.myInt)") // 2
standAloneInOutFunction(&a) 
    // value copy a->b, modify(b), value copy back b->a
print("\(a.myInt)") // 3 

但是请注意,inout关键字并不意味着我们通过引用(就像类实例一样),但我们只是发送一个值副本并将另一个值复制回来,我们最终分配给原始值类型调用参数(a)。

来自Apple documentation on this keyword

  

...输入输出参数具有传递给函数的值,   由函数修改,并从函数传递回去   替换原始值。