type generic functions associated ios swift functional-programming

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