"var"参数已被弃用并将在Swift 3中删除。

132

好的,所以我刚刚将Xcode更新到7.3,现在我收到了这个警告:

'var'参数已过时,将在Swift 3中被删除

当我需要在这个函数中使用var时,如何解决这个问题:

public func getQuestionList(var language: String) -> NSArray {
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}

6
如何翻译 public func getQuestionList(inout language: String) -> NSArray?公共函数 getQuestionList(inout language: String) -> NSArray ,其中 inout language: String 表示该函数将会读取并修改调用方传入的 language 变量。返回值为 NSArray 类型。 - TotoroTotoro
3
不,这不是一个合适的替代方案。OP可能不希望getQuestion有任何副作用。 - BallpointBen
7
我真的不明白为什么他们会考虑移除这个功能。它是使Swift变得很棒的一个特性! - Danny Bravo
我自己从未使用过它,也不理解人们为什么这么关注它。 - Mike Taverne
@kevin 你认为像那样的中间变量不会被优化掉吗?或者你所说的“慢”是什么意思? - Andreas
显示剩余3条评论
9个回答

109
这份提交在GitHub中完整记录了关于从函数参数中删除Var的讨论:删除Var参数
在该文档中,您会发现人们经常将var参数与inout参数混淆。 var参数仅意味着在函数上下文中该参数是可变的,而使用inout参数时,在返回点时参数的值将被复制到调用者的上下文中。
解决这个问题的正确方法是从参数中删除var并引入一个本地var变量。 在程序的顶部,将参数的值复制到该变量中。

53
我完全不理解这个更改,为什么要写另一行来创建可变本地变量比将参数定义为变量更好? - Ross Barbish
4
我同意@RossBarbish的观点。那么...这是因为懒惰的开发人员无法区分inout和var参数而被移除了?哼... - Danny Bravo
3
这似乎非常不必要,他们应该保留两个选项。 - Oscar Gomez
真的是一个很糟糕的解决方案。一个 var 函数参数不会写回到原始输入参数是很合理的逻辑。很容易理解它不是一个 inout 的东西。然而,现在我必须写一行额外的代码 var name = name,只为了能够在函数内部改变参数。我认为这种情况会经常发生。 - Peterdk
1
可能 Swift 在幕后已经声明了一个局部变量覆盖了参数。现在我们必须手动完成它。性能没有改变,但我们失去了帮助初学者理解简单概念的便利。 - mogelbuster
显示剩余4条评论

86

你尝试过将其分配给新变量吗?

public func getQuestionList(language: String) -> NSArray {
    var lang = language
    if self.data.count > 0 {
        if (lang.isEmpty) {
            lang = "NL"
        }
        return self.data.objectForKey("questionList" + lang) as! NSArray
    }

    return NSArray()
}

11
不完全符合原帖作者的意思。 - brimstone
6
我会像@garana一样理解OP的问题。OP在问题中没有使用input,他们只是局部地改变了一个已存在的变量。 - Eric Aya
12
这实际上是正确的解决方案。请参阅提出此更改的Swift进化问题:https://github.com/apple/swift-evolution/blob/master/proposals/0003-remove-var-parameters.md - Scott Thompson
9
@TimVermeulen:每个人都希望使用一种先进的语言。苹果可以通过多种方式发展它们的语言,而不是每个月改变语法。众所周知,由于苹果的原因,大量在线文档和代码片段已经过期或者过时了。开发者们不得不一遍又一遍地提出许多愚蠢的问题来寻求帮助。如果苹果想要更多的开发者精通该语言,那么语法必须从一开始就非常稳定。 - TomSawyer
30
如果您不想引入另一个变量名(这是 var 参数的主要优点,我认为),则可以使用 var language = language。请注意保持原意,使语言通俗易懂。 - Elijah
显示剩余8条评论

67

只需要在函数开头添加这一行:

var language = language

而你的其余代码可以保持不变,像这样:

public func getQuestionList(language: String) -> NSArray {
    var language = language
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}

5
迄今为止最好的答案,只需要更改一行。 - BallpointBen
1
但是看起来非常不自然 @James - asyncwait
1
我觉得这是最好的答案,因为它保持了相同的名称。就像其他常见的编程语言一样。 - Sentry.co
1
@RiverSatya 为什么不直接使用参数? - Declan McKenna
1
非常棒的建议。我们将在Swiftify中按照这种方式实现 :) - Crulex
显示剩余4条评论

14
很多人建议使用 inout 参数,但这并不是它们设计的目的。此外,它不允许使用 let 常量或字符串字面量调用函数。为什么不在函数签名中添加默认值呢?
public func getQuestionList(language language: String = "NL") -> NSArray {
    if data.count > 0 {
        return data.objectForKey("questionList" + language) as! NSArray
    } else {
        return NSArray()
    }
}

请确保在调用getQuestionList时不要使用空字符串,以获取默认语言,请省略该参数:

let list = getQuestionList() // uses the default "NL" language

4
我不明白为什么大家要支持inout解决方案,因为最初OP根本没有使用它... - Eric Aya
1
他们假设var和inout是一样的东西。 - ryantxr

2
public func getQuestionList(language: inout String) -> NSArray {
if self.data.count > 0 {
    if (language.isEmpty) {
        language = "NL"
    }
    return self.data.objectForKey("questionList" + language) as! NSArray
}

return NSArray()

}


https://code.tutsplus.com/tutorials/swift-from-scratch-function-parameters-types-and-nesting--cms-23056 - Abdul Rahman Khan

0

我认为@Harris和@garanda的回答是最好的方法。

无论如何,在您的情况下,不需要一个var,您可以这样做:

public func getQuestionList(language: String) -> NSArray {
    if self.data.count > 0 {
        return self.data.objectForKey("questionList" + (language.isEmpty ? "NL" : language)) as! NSArray
    }
    return NSArray()
}

0

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html

输入输出参数

函数参数默认为常量。试图从函数体内部更改函数参数的值将导致编译时错误。这意味着您不能错误地更改参数的值。如果您希望函数修改参数的值,并且希望在函数调用结束后这些更改仍然存在,请将该参数定义为输入输出参数。

通过在参数类型之前放置inout关键字来编写输入输出参数。输入输出参数具有传入函数的值,由函数修改,并传回函数以替换原始值。有关输入输出参数行为和相关编译器优化的详细讨论,请参见输入输出参数。

您只能将变量作为输入输出参数的参数传递。您无法将常量或文字值作为参数传递,因为常量和文字值无法修改。当您将变量作为输入输出参数的参数传递时,在变量名称之前直接放置“&”符号,以表示函数可以修改它。

注意事项

输入输出参数不能有默认值,可变参数不能标记为inout。

这是一个名为swapTwoInts(::)的函数示例,它有两个in-out整数参数a和b:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

swapTwoInts(::) 函数简单地将 b 的值交换到 a 中,将 a 的值交换到 b 中。该函数通过将 a 的值存储在一个名为 temporaryA 的临时常量中,将 b 的值分配给 a,然后将 temporaryA 分配给 b 来执行此交换。

您可以使用两个 Int 类型的变量调用 swapTwoInts(::) 函数来交换它们的值。请注意,当将 someInt 和 anotherInt 的名称传递给 swapTwoInts(::) 函数时,它们的名称前面带有一个 & 符号:

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

上面的示例显示,swapTwoInts(:)函数修改了一些整数和另一个整数的原始值,尽管它们最初是在函数之外定义的。

注意

传入输出参数与从函数返回值并不相同。上面的swapTwoInts示例没有定义返回类型或返回值,但它仍然修改了someInt和anotherInt的值。传入输出参数是函数在其函数体范围之外具有影响的替代方式。


0

我认为你应该使用 inout,遵循this discussion 给出的建议。

为了更好地理解,在你的例子中:

public func getQuestionList(var language: String) 

应该被替换为:

public func getQuestionList(language: inout String) 

问题的前两个评论解释了为什么这可能是一个糟糕的修复方法:过去在函数内部对变量(此处为“language”)所做的任何更改都不会影响调用程序中的变量——这是使用 var 关键字设计的,而使用 inout 则会影响。在同时存在 varinout 的世界中,如果选择使用其中之一,那么只是将另一个替换进来将导致事情不能按预期工作。如果无法使用 var 关键字,则有一个选择:根据所需更改的范围使用 inout'var lang = language' 方法。 - ConfusionTowers
我不是在谈论重构,而是更多地将其用作所需的行为。 - SHANNAX

0
这里有另一个想法。我的用例是传递一个字符串数组以进行附加,因此必须可变地传递该数组。我也不想在我的类中为此设置状态。因此,我创建了一个包含数组的类并传递它。根据您的用例,拥有仅持有一个变量的类可能看起来很愚蠢。
private class StringBuilder {
    var buffer: [String] = []

    func append(_ str: String) {
        buffer.append(str)
    }

    func toString() -> String {
        return buffer.joined()
    }
}

我仅使用数组上的appendjoined方法,因此更改类型只需进行最小的其他代码更改。

一些示例用法:

private func writeMap(map: LevelMap, url: URL) -> Bool {
    let buffer = StringBuilder()

    if !writeHeader(map: map, buffer: buffer) {
        return false
    }
    if !writeFloors(map: map, buffer: buffer) {
        return false
    }

    let content = buffer.toString()
    do {
        try content.write(to: url, atomically: true, encoding: .utf8)
        return true
    } catch {}
    return false
}

private func writeHeader(map: LevelMap, buffer: StringBuilder) -> Bool {
    buffer.append("something here ...\n")
    return true
}

如果你想要修改调用方所看到的原始值,那么我的答案是可以的。如果你仅希望能够在本地重新分配该值但不影响调用方,则其他上面的答案可以处理这个问题。 - webjprgm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接