ios - functions - Diferencia entre Generics y AnyObject en Swift
swift associated type (3)
Considere esta función myFilter
que toma un argumento genérico y filtra la matriz en función del predicado. Esto es lo mismo que la función filter()
proporcionada por Swift.
func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] {
var result = [T]()
for i in source {
if predicate(i) {
result.append(i)
}
}
return result
}
¿Cómo es esto diferente?
func myFilter(source: [AnyObject], predicate:(AnyObject) -> Bool) -> [AnyObject] {
var result = [AnyObject]()
for i in source {
if predicate(i) {
result.append(i)
}
}
return result
}
¿No estamos alcanzando el punto de genéricos incluso en el último ejemplo?
Considere que en la primera función T no es un tipo, como AnyObject, sino una variable de tipo ; esto significa que en la primera función puede pasar una matriz de valores de cualquier tipo como primer parámetro, pero un predicado que opera solo en valores de ese tipo específico como segundo parámetro. Es decir, puede pasar una serie de cadenas y un predicado en cadenas, o una matriz de enteros y un predicado en enteros, mientras que no puede pasar una matriz de enteros y un predicado en cadenas. Por lo tanto, se garantiza que el cuerpo de la función sea correcto para lo que concierne a los tipos.
En cambio, en el segundo ejemplo puede pasar un valor de cualquier tipo y un predicado que opere en cualquier tipo (¡posiblemente diferente!), De modo que, si el predicado fuera llamado en el cuerpo de la función con el valor del primer parámetro, entonces podría ocurrir un error de tipo dinámico. Afortunadamente, el plugin de tipo Swith marca la llamada del predicado como tipo de error, para evitar esta posibilidad.
Los genéricos son seguros, lo que significa que si pasa una cadena como un genérico y trata de usar como un entero, el compilador se quejará y no podrá compilar (lo cual es bueno). (Esto sucede porque Swift usa tipado estático y puede proporcionarle un error de compilación )
Si usa AnyObject, el compilador no tiene idea si el objeto puede tratarse como una Cadena o como un Entero. Te permitirá hacer lo que quieras con él (lo cual es malo).
por ejemplo, si intentas pasar un String cuando es el Integer utilizado anteriormente, la aplicación se bloqueará. (Esto sucede porque Swift usa tipeo dinámico y solo le dará un bloqueo en tiempo de ejecución )
Genéricos básicamente le dice al compilador:
"Voy a darle un tipo más tarde y quiero que aplique ese tipo en todos los lugares que especifique".
AnyObject básicamente le dice al compilador:
"No te preocupes por esta variable, no es necesario imponer ningún tipo aquí, déjame hacer lo que yo quiera".
Nota : la respuesta de Icaro seguirá siendo la respuesta aceptada, solo estoy extendiendo su explicación.
TL; DR : Verifique la respuesta de Icaro.
Sobre el uso de AnyObject
Icaro pone correctamente:
No se preocupe por esta variable, no es necesario imponer ningún tipo aquí, déjeme hacer lo que quiera.
¿Qué significa esto? Tomemos el ejemplo del código en la pregunta (He subido un escalón y cambiado AnyObject
a Any
sin cambiar el significado de la pregunta):
func myFilter(source: [Any], predicate:(Any) -> Bool) -> [Any] {
var result = [Any]()
for i in source {
if predicate(i) {
result.append(i)
}
}
return result
}
Esto significa que la función myFilter
toma en dos argumentos una source
y un predicate
cierre. Si miras de cerca, el contenido de la matriz fuente puede ser CUALQUIER COSA. Y el argumento del predicate
cierre también puede ser CUALQUIER COSA. Si tuviéramos que nombrar estos "CUALQUIER COSA" - digan CUALQUIER COSA1 y CUALQUIER COSA2 - este enfoque no requiere NADA_1 que sea igual a CUALQUIERA2.
Sentémonos y reflexionemos sobre las implicaciones de esto ...
Digamos, queremos filtrar evens de una matriz de enteros y usemos Any
filtro para este
var ints = [1,2,3,4,5] as [Any]
var predicate = { (a : Any) -> Bool in
return (a as! Int) % 2 == 0
}
let evens = myFilter(source: ints, predicate:predicate)
Wow, eso funcionó, ¿no? ¿Todo sonrisas? No.
Observe cómo en la línea:
return (a as! Int) % 2 == 0
Estoy forzando a la fuerza a
. Esta línea se bloqueará si a
fuera algo más que un Int
. Pero su uso está justificado; después de todo, queremos filtrar los Int
y soy lo suficientemente inteligente como para usar solo una variedad de Int
.
Pero, como digo, soy un programador ingenuo, hago esto:
var ints = [1,2,3,4,5,"6"] as [Any]
var predicate = { (a : Any) -> Bool in
return (a as! Int) % 2 == 0
}
let evens = myFilter(source: ints, predicate:predicate)
Esto se compila felizmente, pero se bloquea en el tiempo de ejecución. Si tan solo hubiera una manera, donde el compilador me dijera que esta línea ...
var ints = [1,2,3,4,5,"6"]
... fue defectuoso, no hubiéramos tenido un accidente. Lo habría arreglado de inmediato!
Resulta que sí. Genéricos Para citar a Icaro nuevamente,
Le daré un tipo más tarde y quiero que aplique ese tipo en todos los lugares que especifique.
func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] {
var result = [T]()
for i in source {
if predicate(i) {
result.append(i)
}
}
return result
}
var ints = [1,2,3,4,5,6]
var predicate = { (a : Int) -> Bool in
return a % 2 == 0
}
let evens = myFilter(source: ints, predicate:predicate)
Este nuevo filtro es increíble. No me deja hacer:
let evens = myFilter(source: ints, predicate:predicate)
porque los tipos del predicate
y la source
no coinciden. Error de tiempo de compilación.
Generics es genérico de esta manera: en este ejemplo específico: mientras escribes la función myFilter
, no necesitas dar un tipo de la source
o el argumento que toma el predicate
, es T, es NADA. Pero una vez que digo que la source
es una matriz de CUALQUIER COSA, TIENES que asegurarte de que el argumento de que el predicate
acepta es el mismo NADA. Con el trasfondo de nuestra anterior nomenclatura ANYTHING1, ANYTHING2, podemos decir que los genéricos fuerzan CUALQUIERA1 a ser iguales a CUALQUIERGO2