Los parámetros 'var' están obsoletos y se eliminarán en Swift 3

120

Muy bien, acabo de actualizar Xcode a 7.3 y ahora recibo esta advertencia:

Los parámetros 'var' están obsoletos y se eliminarán en Swift 3

Cómo solucionar esto cuando necesito usar la var en esta función:

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()
}
SDW
fuente
6
Qué talpublic func getQuestionList(inout language: String) -> NSArray
TotoroTotoro
2
No, este no es un reemplazo adecuado. OP probablemente no quiere getQuestiontener efectos secundarios.
BallpointBen
5
Honestamente, no tengo idea de por qué considerarían eliminar esto. ¡Fue una de las características que hizo que Swift fuera increíble!
Danny Bravo
Nunca lo usé yo mismo y no entiendo el alboroto.
Mike Taverne
@MikeTaverne (respuesta tardía) Considere la siguiente función: func foo(_ bar: int) { /*use bar*/ bar+=1; foo(bar); }. Esto es imposible sin var params. Necesita crear una var separada dentro de la función y copiar el valor, o marcar el parámetro como inout. El primero es lento, el segundo provoca un comportamiento indefinido. Muchos algoritmos usan la recursividad como esta.
kevin

Respuestas:

82

¿Ha intentado asignar a una nueva var

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()
}
garanda
fuente
11
No es realmente lo que creo que quería el OP
azufre
6
Habría entendido la pregunta de OP de la misma manera que @garana. OP no usa inout en su pregunta, solo mutan una variable preexistente localmente .
Eric Aya
11
De hecho, esta es la solución correcta. Consulte el problema de Swift Evolution
Scott Thompson
8
@TimVermeulen Todo el mundo quiere usar un lenguaje progresivo. Apple puede desarrollar su lenguaje de muchas formas, no cambiando la sintaxis cada mes. Como usted sabe, una tonelada de documentos y fragmentos de código en línea han caducado o están desactualizados debido a Apple. Los desarrolladores tienen que venir a este sitio para pedir ayuda con muchas preguntas estúpidas repetidamente debido a esto. La sintaxis debe ser sólida desde el principio si Apple quiere que más desarrolladores sean buenos en eso.
TomSawyer
25
Use var language = language, si no desea introducir otro nombre de variable (que fue la principal ventaja del parámetro var en primer lugar, en mi opinión)
Harris
102

La discusión sobre la eliminación de Var de un parámetro de función está completamente documentada en este envío en GitHub: Eliminar parámetros de Var

En ese documento encontrará que las personas a menudo confunden varparámetros con inoutparámetros. Un varparámetro simplemente significa que el parámetro es mutable dentro del contexto de la función, mientras que con un inoutparámetro el valor del parámetro en el punto de retorno se copiará fuera de la función y en el contexto de la persona que llama.

La forma correcta de resolver este problema es eliminar vardel parámetro e introducir una varvariable local . En la parte superior de la rutina, copie el valor del parámetro en esa variable.

Tr0yJ
fuente
44
No entiendo este cambio en absoluto, ¿por qué tener que escribir otra línea para crear una var local mutable sería mejor que simplemente definir el parámetro como una var?
Ross Barbish
Para mí, este cambio es bueno porque está detectando situaciones en las que debería haber implementado una variable local, pero no lo hice porque tomé el camino más fácil y acepté la sugerencia (antigua) de Swift de hacer que el parámetro de entrada sea var
dawid
1
Estoy con @RossBarbish en esto. Entonces ... ¿esto se eliminará porque los desarrolladores perezosos no pueden distinguir entre los parámetros inout y var? Pfff ...
Danny Bravo
1
Esto parece terriblemente innecesario ..., deberían haber mantenido ambas opciones.
Oscar Gomez
1
Probablemente Swift estaba declarando una variable local por encima del parámetro detrás de escena de todos modos. Ahora tenemos que hacerlo manualmente. No hubo cambios en el rendimiento, pero perdimos la conveniencia para ayudar a los principiantes con un concepto simple.
mogelbuster
62

Simplemente agregue esta línea al comienzo de la función:

var language = language

y el resto de su código puede permanecer sin cambios, así:

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()
}
Harris
fuente
5
La mejor respuesta de lejos. Solo requiere cambiar una línea.
BallpointBen
Pero parece tan antinatural @James
asyncwait
1
Siento que esta es la mejor respuesta ya que mantiene el mismo nombre. Similar a cómo lo hacen otros lenguajes comunes.
eonista
1
@RiverSatya ¿Por qué no usar el parámetro directamente?
Declan McKenna
1
Realmente una sugerencia increíble. Lo implementaremos de esta manera en Swiftify :)
Crulex
13

Mucha gente está sugiriendo un inoutparámetro, pero en realidad no está diseñado para eso. Además, no permite llamar a la función con una letconstante, ni con un literal de cadena. ¿Por qué no agrega simplemente el valor predeterminado a la firma de la función?

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

Solo asegúrese de no llamar getQuestionListcon la cadena vacía en caso de que desee el idioma predeterminado, pero simplemente omita el parámetro:

let list = getQuestionList() // uses the default "NL" language
Tim Vermeulen
fuente
3
Tampoco entiendo por qué todos se lanzaron a la solución inout cuando OP ni siquiera estaba usando eso al principio ...
Eric Aya
1
Suponían que var e inout hacían lo mismo.
ryantxr
2

Rápido 4

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

getQuestionList(language: &someString)

En algunos casos, como he experimentado (con configuraciones más complejas que involucran matrices), es posible que la creación de una nueva propiedad dentro del método y la mutación de esa propiedad no siempre funcione. Sin mencionar que está saturando el método en lugar de simplemente agregar inoutun parámetro y &su argumento, que es para lo que se creó esta sintaxis.

bsod
fuente
1
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()

}

Abdul Rahman Khan
fuente
0

Creo que las respuestas de @Harris y @garanda son el mejor enfoque.

De todos modos, en su caso, no es necesaria una var, puede hacer:

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

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

Parámetros de entrada y salida

Los parámetros de función son constantes por defecto. Intentar cambiar el valor de un parámetro de función desde dentro del cuerpo de esa función da como resultado un error en tiempo de compilación. Esto significa que no puede cambiar el valor de un parámetro por error. Si desea que una función modifique el valor de un parámetro y desea que esos cambios persistan después de que finalice la llamada a la función, defina ese parámetro como un parámetro de entrada-salida.

Escribe un parámetro in-out colocando la palabra clave inout justo antes del tipo de parámetro. Un parámetro de entrada-salida tiene un valor que se pasa a la función, es modificado por la función y se devuelve fuera de la función para reemplazar el valor original. Para obtener una descripción detallada del comportamiento de los parámetros de entrada y salida y las optimizaciones del compilador asociadas, consulte Parámetros de entrada y salida.

Solo puede pasar una variable como argumento para un parámetro de entrada-salida. No puede pasar una constante o un valor literal como argumento, porque las constantes y los literales no se pueden modificar. Coloca un ampersand (&) directamente antes del nombre de una variable cuando lo pasa como un argumento a un parámetro de entrada-salida, para indicar que la función puede modificarlo.

NOTA

Los parámetros de entrada y salida no pueden tener valores predeterminados y los parámetros variables no se pueden marcar como inout.

Aquí hay un ejemplo de una función llamada swapTwoInts ( : :), que tiene dos parámetros enteros de entrada y salida llamados ayb:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

La función swapTwoInts ( : :) simplemente intercambia el valor de b en a, y el valor de a en b. La función realiza este intercambio almacenando el valor de a en una constante temporal llamada temporalA, asignando el valor de b a a, y luego asignando temporalA a b.

Puede llamar a la función swapTwoInts ( : :) con dos variables de tipo Int para intercambiar sus valores. Tenga en cuenta que los nombres de someInt y anotherInt tienen el prefijo ampersand cuando se pasan a la función 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"

El ejemplo anterior muestra que los valores originales de someInt y anotherInt son modificados por la función swapTwoInts ( : :), aunque originalmente se definieron fuera de la función.

NOTA

Los parámetros de entrada y salida no son lo mismo que devolver un valor de una función. El ejemplo de swapTwoInts anterior no define un tipo de retorno ni devuelve un valor, pero aún modifica los valores de someInt y anotherInt. Los parámetros de entrada y salida son una forma alternativa de que una función tenga un efecto fuera del alcance de su cuerpo de función.

Mustafa Mohammed
fuente
0

Aquí hay otra idea. Mi caso de uso fue pasar una matriz de cadenas para agregarla, para lo cual la matriz debe pasarse de manera mutante. Tampoco quería tener un estado en mi clase para esto. Así que hice una clase que contiene la matriz y la paso. Dependiendo de su caso de uso, puede parecer una tontería tener una clase que contenga solo esa variable.

private class StringBuilder {
    var buffer: [String] = []

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

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

Yo solo uso appendjoined métodos y en la matriz, por lo que fue fácil cambiar el tipo con un mínimo de otros cambios en mi código.

Algunos ejemplos de uso:

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
fuente
Mi respuesta es si DESEA que se modifique el valor original tal como lo ve la persona que llama. Si solo desea poder reasignar localmente el valor pero no afectar a la persona que llama, otras respuestas anteriores tratan con eso.
webjprgm