¿Dónde se definen las variables de clase en Smalltalk?
Me preguntaba, si defino una nueva variable de clase, por ejemplo para la clase MyClass
, ¿la definición estará dentro MyClass
o dentro MyClass class
? ¿ MyClass class
Sabe siquiera sobre la nueva variable de clase?
Respuestas
Sí, las variables de clase se comparten con la clase y la metaclase. También se comparten con todas las subclases (y sus metaclases). Una variable de clase suele estar en mayúscula, para transmitir mejor la idea de que se comparte en un ámbito más amplio que la clase. Las variables de clase se definen en la clase (no en la metaclase).
Las variables de clase no deben confundirse con las variables de instancia de clase, que son variables de instancia definidas a nivel de metaclase, es decir, variables de instancia del objeto de clase. Esta noción es algo oscura a pesar de su simplicidad (o debido a ella): las variables de instancia siempre se definen en la clase para definir la forma (ranuras) de sus instancias . Por lo tanto, si aplicamos esta definición a la metaclase, que es la clase de la clase, una variable de instancia definida aquí define la forma de sus instancias, de las cuales (generalmente) solo hay una, la clase.
Volviendo a las variables de clase, las define en la clase (lado inst) y las inicializa en la metaclase (es decir, el lado de la clase). Recuerde que estos son globales (parciales) en el sentido de que serán compartidos entre instancias, subinstancias, subclases y metaclases y por lo tanto deben manejarse con el cuidado habitual que tratamos los globales.
Una aclaración más
Cuando decimos que las variables de instancia se comparten entre instancias y subinstancias, nos referimos a sus nombres (y posiciones en la memoria de las ranuras de objetos); no nos referimos a sus valores (contenido de dichos espacios). Por lo tanto, dos instancias de la clase C
compartirán el nombre, digamos color
, si la clase define el ivar color
, pero sus valores en cada una de las instancias serán independientes. En otras palabras, lo que se comparte es el nombre, no el valor.
Con las variables de clase, lo que se comparte es tanto el nombre como el valor. En realidad, es el Association
objeto, por ejemplo Theme -> aTheme
, lo que se comparte. En consecuencia, cualquier modificación del valor de una variable de clase afecta a todas sus referencias. Este no es el caso de las variables de instancia de clase porque no son más que variables de instancia, excepto que dan forma a la clase y sus subclases, en lugar de instancias y subinstancias regulares.
Para obtener más información sobre las variables de Smalltalk, consulte https://stackoverflow.com/a/42460583/4081336
Solo como complemento a la respuesta de Leandro, aquí está el método específico de implementación principal de Squeak que explica el intercambio de variables de clase entre el lado de la instancia (clase) y el lado de la clase (metaclase):
Metaclass>>classPool
"Answer the dictionary of class variables."
^thisClass classPool
donde thisClass
es la instancia única de la Metaclase, que es la clase misma ...
Sin embargo, hay muchas posibilidades de encontrar una implementación similar en la mayoría de los dialectos de Smalltalk.
El compilador primero intentará resolver la variable como un método / bloque temporal (incluidos los parámetros del método / bloque), luego las variables de instancia y luego las variables compartidas.
El compilador envía el método classPool en esta última fase.
Como explicó Leandro, el compilador resuelve el enlace solo como un desplazamiento que se transcribirá directamente en el código de bytes en el caso de la ranura de la variable de instancia o la variable temporal del método, o como una especie de Asociación para el caso de la variable compartida, siendo esta asociación generalmente agregado a los literales CompiledMethod y efectivamente compartido entre todos los métodos que tratan con esta variable (todos los métodos apuntan al mismo objeto Assocation que se comparte efectivamente).
La parte del compilador es mucho más específica del dialecto, en Squeak, es este método el que se usa para resolver el enlace de variables compartidas:
class>>bindingOf: varName environment: anEnvironment
"Answer the binding of some variable resolved in the scope of the receiver"
| aSymbol binding |
aSymbol := varName asSymbol.
"First look in local classVar dictionary."
binding := self classPool bindingOf: aSymbol.
binding ifNotNil:[^binding].
"Next look in local shared pools."
self sharedPools do:[:pool |
binding := pool bindingOf: aSymbol.
binding ifNotNil:[^binding].
].
"Next look into superclass pools"
superclass ifNotNil: [^ superclass bindingOf: aSymbol environment: anEnvironment].
"No more superclass... Last look in declared environment."
^anEnvironment bindingOf: aSymbol
Esto es para recordarle que una de las partes más interesantes de Smalltalk es que puede profundizar en la implementación desde dentro del IDE, ¡Smalltalk está esencialmente escrito en Smalltalk!