the - Swift 3 silenciosamente permite sombrear un parámetro
swift wikipedia (4)
Estoy cambiando a Swift, y realmente no estoy contento de que el siguiente código se compile sin una advertencia:
func f(_ x: inout Int?) {
var x: Int? // <-- this declaration should produce a warning
x = 105
if x! < 1000 {}
}
var a: Int? = 3
f(&a)
print("/(a)")
y, por supuesto, salidas Optional(3)
en la ejecución.
En este ejemplo, la variable local x
sombrea el parámetro de función x
.
Al GCC_WARN_SHADOW
advertencia de Hidden Local Variables
( GCC_WARN_SHADOW
) en la configuración del proyecto, tampoco se genera una advertencia.
Pregunta: ¿Cómo debo proceder para hacer que el compilador Swift 3 me advierta sobre este tipo de sombras?
Con respecto al sufijo opcional y el parámetro inout, en un caso como este, deberías manejarlo más así:
func f(_ x: inout Int?)
{
guard let guardedX = x else
{
return
}
... Rest of method
if x != nil
{
x = guardedX
}
}
- Esto evitará que (opcional) al imprimir.
- Esto evitará el bloqueo que ocurrirá en su cheque
if x! < 1000
if x! < 1000
cuando alguien cambia de x a nula!
Debe revisar la coincidencia de patrones rápida y cualquier cosa que tenga que ver con el desenvolvimiento y el encadenamiento y la Concurrency opcionales (no específicos de Swift) para asegurarse de escribir un código limpio y ordenado.
Edite el nombre X (en valor de parámetro o variable) a otro nombre
ejemplo
func f(_ x: inout Int?) {
var y: Int?
y = 105
if x! < 1000
{
x = y
}
}
var a: Int? = 3
f(&a)
print("/(a)")
Esto también está ocurriendo para los parámetros que tampoco son InOut
, es decir:
func testShadowParam(number: Int) {
let number: Int = 3 // also does not produce a warning
print(number)
}
Este es un error del compilador documentado: bugs.swift.org/browse/SR-1687
Si bien es posible que ya hayas encontrado soluciones útiles, la Documentación sobre funciones de Apple tiene un comentario sobre este tipo exacto de uso. Usted solicitó una respuesta por la cual el resaltado de código no le advierte del conflicto de nombres, pero la razón principal por la cual probablemente no recibe ninguna advertencia es porque los parámetros de entrada y todos los parámetros no tienen prioridad sobre las variables inicializadas dentro de la función ( solo son copias del valor que representan cuando se manipulan dentro de la función). Por lo tanto, su función, como ilustraré a continuación, no tiene en cuenta el parámetro que está pasando, porque inicializa una nueva variable con el mismo nombre. Por lo tanto, según las reglas que gobiernan los parámetros, su parámetro pasado se ignora por completo. Veo su frustración, ya que en otros idiomas esto sería un error de compilación. Sin embargo, con inouts aquí, esto simplemente no es el caso como una convención. Ver aquí en los docs :
Los parámetros de entrada y salida se pasan de la siguiente manera:
Cuando se llama a la función, se copia el valor del argumento. En el cuerpo de la función, se modifica la copia. Cuando la función vuelve, el valor de la copia se asigna al argumento original. Este comportamiento se conoce como copia-en-copia o salida por valor de resultado. Por ejemplo, cuando una propiedad calculada o una propiedad con observadores se pasa como un parámetro de entrada / salida, se llama a su captador como parte de la llamada a la función y se llama a su configurador como parte del retorno de la función.
Como optimización, cuando el argumento es un valor almacenado en una dirección física en la memoria, se utiliza la misma ubicación de memoria tanto dentro como fuera del cuerpo de la función. El comportamiento optimizado se conoce como llamada por referencia; satisface todos los requisitos del modelo de copiado de copiado y elimina la sobrecarga de copiado. Escriba su código usando el modelo dado por el copiado entrante, sin depender de la optimización llamada por referencia, para que se comporte correctamente con o sin la optimización.
No acceda al valor que se pasó como un argumento de entrada / salida, incluso si el argumento original está disponible en el ámbito actual. Cuando la función vuelve, sus cambios al original se sobrescriben con el valor de la copia. No dependa de la implementación de la optimización llamada por referencia para intentar evitar que los cambios se sobrescriban. [..]
En su caso, si realmente modificara el parámetro que estaba pasando, usaría algo similar a esto:
Si necesita capturar y mutar un parámetro de entrada-salida, use una copia local explícita, como en un código de multiproceso que asegure que toda la mutación haya finalizado antes de que la función regrese.
func multithreadedFunction(queue: DispatchQueue, x: inout Int) {
// Make a local copy and manually copy it back.
var localX = x
defer { x = localX }
// Operate on localX asynchronously, then wait before returning.
queue.async { someMutatingOperation(&localX) }
queue.sync {}
}
Como puede ver aquí, mientras que localX no se llama x, como lo hace, localX toma una instancia de memoria completamente diferente para contener datos. Que en este caso es el mismo valor que x, pero no es la instancia de x, por lo que no se compila como un error de nombre. Para mostrar que esto sigue siendo válido cuando cambia localX a var x = Int? como haces dentro de tu función:
func f(_ x: inout Int?) {
print(x, "is x")
var x: Int? // <-- this declaration should produce a warning
print(x, "is x after initializing var x : Int?")
x = 105
print(x, "is x after giving a value of 105")
if x! < 1000 {}
}
var a: Int? = 3
f(&a)
print("/(a)", "is x after your function")
Devoluciones:
Optional(3) is x
nil is x after initializing var x: Int?
Optional(105) is x after giving a value of 105 to x
Optional(3) is x after your function
Para mostrarle hasta dónde llega esto, usaré lo que hizo Mohsen para mostrarle que no estaba completamente equivocado en su lógica para mostrarle esta regla en la convención, aunque estoy de acuerdo en que no abordó la falta de advertencia de código. en tu pregunta
func f(_ x: inout Int?) {
print(x, "is inout x")
var y: Int? // <-- this declaration should produce a warning
print(x, "is inout x and ", y, "is y")
x = 105
print(x, "is inout x and ", y, "is y after giving a value of 105 to inout x")
if x! < 1000 {}
}
var a: Int? = 3
f(&a)
print("/(a)", "is x after your function")
Huellas dactilares:
Optional(3) is inout x
Optional(3) is inout x and nil is y
Optional(105) is inout x and nil is y after giving a value of 105 to inout x
Optional(105) is x after your function
Entonces, como puede ver aquí en la primera función, sus parámetros de entrada y parámetros en general, dejan de tener prioridad sobre lo que está contenido dentro, porque técnicamente no tiene inicialización dentro de la función, que es el propósito de la convención inout en sí misma: la función guarda ese valor en la memoria, asigna un puntero a la instancia de memoria, y cualquier mutación que se aplique a ese puntero se aplica a la variable original que está fuera del alcance de la función cuando la función termina. Entonces, ¿qué mutaciones podrías haberle hecho después de var x: Int?
no alterará la variable en su parámetro de entrada cuando se golpea el return
, porque ha reemplazado el puntero asignado a la letra x. Para mostrarle que este no es el caso con non-inouts
, le asignaremos una variable distinta de x:
func f(_ x: Int?) {
print(x!, "is inout x")
var y: Int? // <-- this declaration should produce a warning
print(x!, "is inout x and ", y!, "is y")
x = 105
y = 100
print(x!, "is inout x and ", y!, "is y after giving a value of 105 to inout x")
if x! < 1000 {}
}
var a: Int? = 3
f(a)
print("/(a!)", "is x after your function")
Devoluciones
Playground execution failed: error: SomeTest.playground:6:7: error: cannot assign to value: ''x'' is a ''let'' constant
x = 105
Pero, si vuelvo a la función original y renombro la nueva variable al mismo puntero que el nombre del parámetro:
func f(_ x: Int?) {
print(x, "is inout x")
var x: Int? // <-- this declaration should produce a warning
print(x, "is inout x and ")
x = 100
print(x, "is inout x and ")
if x! < 1000 {}
}
var a: Int? = 3
f(a)
print("/(a!)", "is x after your function")
obtenemos:
Optional(3) is inout x
nil is inout x and
Optional(100) is inout x and
3 is x after your function
Entonces, en general, el parámetro inout y el parámetro estándar nunca se modifican, ya que, dentro del alcance de la función, el puntero para x está completamente anulado con Int?
.
Esta es la razón por la que no recibe una advertencia de código y, técnicamente, no debería hacerlo debido a las convenciones que rodean a los parámetros que dictan que lo que escribió no es un conflicto de compilación y es un código válido (tal vez no sea para su caso de uso, pero convencionalmente, lo es) y por lo tanto es muy probable que no pueda encontrar una manera de resaltar este problema de nombres.