Onde as variáveis de classe são definidas em Smalltalk?
Eu queria saber, se eu definir uma nova variável de classe, por exemplo, para a classe MyClass
, a definição vai estar em MyClass
ou em MyClass class
? Ao MyClass class
menos sabe sobre a nova variável de classe?
Respostas
Sim, as variáveis de classe são compartilhadas com a classe e a metaclasse. Eles também são compartilhados com todas as subclasses (e suas metaclasses). Uma variável de classe geralmente é capitalizada, para transmitir melhor a ideia de ser compartilhada em um escopo mais amplo do que a classe. Você define variáveis de classe na classe (não na metaclasse).
Variáveis de classe não devem ser confundidas com variáveis de instância de classe, que são variáveis de instância definidas no nível da metaclasse, isto é, variáveis de instância do objeto de classe. Essa noção é um tanto obscura, apesar de sua simplicidade (ou por causa dela): variáveis de instância são sempre definidas na classe para definir a forma (slots) de suas instâncias . Assim, se aplicarmos essa definição à metaclasse, que é a classe da classe, uma variável de instância definida aqui define a forma de suas instâncias, das quais há (normalmente) apenas uma, a classe.
Voltando às variáveis de classe, você as define na classe (lado inst) e as inicializa na metaclasse (ou seja, lado da classe). Lembre-se de que esses são globais (parciais) no sentido de que serão compartilhados entre instâncias, sub-instâncias, subclasses e metaclasses e, portanto, devem ser tratados com o cuidado usual com que tratamos os globais.
Mais um esclarecimento
Quando dizemos que variáveis de instância são compartilhadas entre instâncias e subinstâncias, queremos dizer seus nomes (e posições na memória dos slots de objetos); não queremos dizer seus valores (conteúdo dos referidos slots). Assim, duas instâncias da classe C
compartilharão o nome, digamos color
, se a classe definir o ivar color
, mas seus valores em cada uma das instâncias serão independentes. Em outras palavras, o que é compartilhado é o nome, não o valor.
Com as variáveis de classe, o que é compartilhado é o nome e o valor. Na verdade, é o Association
objeto, por exemplo Theme -> aTheme
, o que é compartilhado. Consequentemente, qualquer modificação no valor de uma variável de classe afeta todas as suas referências. Este não é o caso com variáveis de instância de classe porque elas nada mais são do que variáveis de instância, exceto que moldam a classe e suas subclasses, em vez de instâncias regulares e subinstâncias.
Para obter mais informações sobre as variáveis Smalltalk, consulte https://stackoverflow.com/a/42460583/4081336
Apenas como um complemento à resposta de Leandro, aqui está o principal método específico de implementação do Squeak que explica o compartilhamento de variáveis de classe entre o lado da instância (classe) e o lado da classe (metaclasse):
Metaclass>>classPool
"Answer the dictionary of class variables."
^thisClass classPool
onde thisClass
está a instância única da Metaclass, que é a própria classe ...
Existem grandes chances de encontrar implementações semelhantes na maioria dos dialetos Smalltalk.
O compilador tentará primeiro resolver a variável como um método / bloco temporário (incluindo parâmetros methd / bloco), depois as variáveis de instância e depois as variáveis compartilhadas.
O método classPool é enviado pelo compilador nesta última fase.
Leandro explicou que o compilador resolve a ligação apenas como um deslocamento que será transcrito diretamente no bytecode no caso de slot de variável de instância ou variável temporária de método, ou como um tipo de associação para o caso de variável compartilhada, sendo esta associação geralmente adicionado aos literais CompiledMethod e efetivamente compartilhado entre todos os métodos que lidam com esta variável (todos os métodos apontam para o mesmo objeto Assocation que é efetivamente compartilhado).
A parte do compilador é muito mais específica do dialeto, no Squeak, é este método que é usado para resolver a ligação de variáveis compartilhadas:
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
Isso é para lembrá-lo de que uma das partes mais interessantes do Smalltalk é que você pode se aprofundar na implementação de dentro do IDE. Smalltalk é essencialmente escrito em Smalltalk!