swift - hoy - joni mitchell kelly dale anderson
¿Por qué no hay una clase base universal en Swift? (2)
La documentación dice
NOTA
Las clases Swift no heredan de una clase base universal. Las clases que defines sin especificar una superclase se convierten automáticamente en clases base sobre las que puedes construir ".
Extracto de: Apple Inc. " El lenguaje de programación Swift. "IBooks.
No tiene mucho sentido para mí. Hay una razón por la cual Objective-C tiene una clase base universal, y la misma razón debería aplicarse a Swift, ¿o sí? NSObject
administra la semántica de retención / liberación, una implementación predeterminada para isEqual:
hash
y description
. Toda esta funcionalidad está disponible también en Swift.
(Objective-C y Swift usan el mismo tiempo de ejecución ...)
Entonces, ¿qué pasa con eso? ¿Las clases Swift sin superclases definidas solo son NSObject
s que se presentan como clases raíz adecuadas debajo del capó? ¿O es el comportamiento de objeto predeterminado duplicado para cada nueva clase raíz? ¿O han creado otra Swift-baseclass? La implementación de retain
y release
es realmente compleja, ya que debe tener en cuenta al mismo tiempo las referencias débiles y multihilo.
¿Existe tal vez una clase base universal en Swift (a pesar de lo que dice la documentación)? Sería realmente útil, porque en Objective-C puedo, por ejemplo, escribir extensiones que me permitan unir invocaciones de métodos al runloop principal como [obj.eventually updateCounter]
que se puede leer como "call- -updateCounter
la próxima vez que se ejecute el runloop principal" control. Si, mientras tanto, vuelvo a llamar a este método, debería llamarse solo una vez. Con esta extensión, se podría implementar -[UIView setNeedsDisplay]
como [self.eventually display];
esto ya no es posible en Swift si hay no es una clase base universal (o tal vez lo es, ¿quién sabe?)
Esta es principalmente una decisión de diseño , hay idiomas que tienen una clase de raíz (por ejemplo, Java) y lenguajes que no (por ejemplo, C ++).
Tenga en cuenta que en Obj-C una clase raíz no se aplica. Puede crear fácilmente un objeto que no hereda de ninguna clase. También puede crear sus propias clases de raíz, hay al menos 3 en la API de Apple ( NSObject
, NSProxy
y Object
obsoleto).
La razón para tener una clase raíz es en su mayoría histórica: la clase raíz garantiza que todos los objetos tengan una interfaz común, algunos métodos comunes (por ejemplo, isEqualTo:
hash()
etc.) que son necesarios para que las clases de colección funcionen.
Una vez que tenga genéricos (o plantillas en C ++), tener una clase raíz ya no es tan importante.
retain
y release
en NSObject
ya no son importantes desde ARC. Con MRC, todavía estaba obligado a llamarlos. Con ARC, nunca llama a los métodos explícitamente y se pueden implementar de manera más eficiente detrás de escena.
En Swift, los métodos de NSObject
se han dividido en protocolos: Equatable
, Hashable
, Printable
y DebugPrintable
. Eso tiene la ventaja de que los objetos pueden compartir interfaces con estructuras.
Sin embargo, no hay nada que te NSObject
heredar cada clase de NSObject
. La clase todavía está allí y es especialmente útil si se trata de API de Obj-C. En Swift puro, una clase raíz no es necesaria sin embargo.
Una nota más:
Las clases Swift no se ejecutan encima de Obj-C; no están traducidos a Obj-C detrás de escena. Solo están compilados por el mismo compilador, lo que les permite interactuar entre ellos. Eso es realmente importante de entender Es por eso que @objc
debe agregar a veces para proporcionar coherencia con los protocolos / clases de Obj-C.
Hay varios lenguajes orientados a objetos donde uno puede definir nuevas clases raíz, incluyendo C ++, PHP y Objective-C, y funcionan bien, por lo que definitivamente no es algo especial.
Hay una razón por la cual Objective-C tiene una clase base universal
Como Sulthan mencionó, esto no es verdad. Hay múltiples clases raíz en Objective-C, y puede definir una nueva clase raíz simplemente sin especificar una superclase. Como también mencionó Sulthan, el propio Cocoa tiene varias clases raíz, NSObject
, NSProxy
y Object
(la clase raíz de Protocol
en ObjC 1.0).
El lenguaje Objective-C original era muy flexible y, en teoría, alguien podría crear y crear su propia clase raíz y crear su propio marco que es completamente diferente de Foundation, y utiliza métodos completamente diferentes de retain
, release
, alloc
, dealloc
, etc. , e incluso podría implementar una forma completamente diferente de administración de memoria si quisiera. Esta flexibilidad es una de las cosas más sorprendentes del lenguaje desnudo Objective-C: simplemente proporciona una capa delgada, todas las otras cosas, como cómo se crean y destruyen los objetos, la gestión de la memoria, etc., todo puede ser determinado por el usuario. marcos sentados en la parte superior.
Sin embargo, con Objective-C 2.0 de Apple y el tiempo de ejecución moderno, se necesitaba hacer más trabajo para crear su propia clase de raíz. Y con la adición de ARC, para usar sus objetos en ARC, debe implementar los métodos de administración de memoria de Cocoa como retain
y release
. Además, para usar sus objetos en las colecciones de Cocoa, su clase también debe implementar cosas como isEqual:
y hash
.
Entonces, en el desarrollo moderno Cocoa / Cocoa Touch, los objetos generalmente deben al menos implementar un conjunto básico de métodos, que son los métodos en el protocolo NSObject
. Todas las clases raíz en Cocoa ( NSObject
, NSProxy
) implementan el protocolo NSObject
.
Entonces, ¿qué pasa con eso? ¿Son las clases Swift sin superclases definidas solo NSObjects que se presentan como clases de raíz adecuadas bajo el capó? ¿O es el comportamiento de objeto predeterminado duplicado para cada nueva clase raíz? ¿O han creado otra Swift-baseclass?
Esta es una buena pregunta, y se puede averiguar por introspección con el tiempo de ejecución de Objective-C. Todos los objetos en Swift son, en cierto sentido, también objetos Objective-C, en el sentido de que se pueden usar con el tiempo de ejecución de Objective-C al igual que los objetos de Objective-C. Algunos miembros de la clase (los que no están marcados como @objc
o dynamic
) pueden no ser visibles para Objective-C, pero de lo contrario todas las características de introspección del tiempo de ejecución de Objective-C funcionan completamente en objetos de clases Swift puras. Las clases definidas en Swift se parecen a cualquier otra clase en el tiempo de ejecución de Objective-C, excepto que el nombre está mutilado.
Usando el tiempo de ejecución de Objective-C, puede descubrir que para una clase que es una clase raíz en Swift, desde el punto de vista de Objective-C, en realidad tiene una superclase llamada SwiftObject
. Y esta clase SwiftObject
implementa los métodos del protocolo NSObject
como retain
, release
, isEqual:
respondsToSelector:
etc. (aunque en realidad no se ajusta al protocolo NSObject
). Así es como puedes usar objetos puros Swift con Cocoa APIs sin problema.
Desde dentro de Swift, sin embargo, el compilador no cree que una clase raíz Swift implemente estos métodos. Entonces, si defines una clase raíz Foo
, entonces si tratas de llamar a Foo().isKindOfClass(Foo.self)
, no compilará quejándose de que este método no existe. Pero aún podemos usarlo con un truco: recuerde que el compilador nos permitirá llamar a cualquier método Objective-C (que el compilador haya escuchado) en una variable de tipo AnyObject
, y la búsqueda de métodos produce una función opcional implícitamente AnyObject
que tiene éxito o falla en tiempo de ejecución. Entonces, lo que podemos hacer es AnyObject
a AnyObject
, asegúrese de importar Foundation
u ObjectiveC
(para que la declaración sea visible para el compilador), luego podemos llamarlo, y funcionará en tiempo de ejecución:
(Foo() as AnyObject).isKindOfClass(Foo.self)
Entonces, básicamente, desde el punto de vista de Objective-C, una clase Swift tiene una clase Objective-C existente como clase raíz (si se hereda de una clase Objective-C), o tiene SwiftObject
como clase raíz.