Где в Smalltalk определены переменные класса?

Jan 19 2021

Мне было интересно, если я определю новую переменную класса, например, для класса MyClass, определение будет внутри MyClassили внутри MyClass class? Знает ли MyClass classвообще о новой переменной класса?

Ответы

5 LeandroCaniglia Jan 20 2021 at 00:36

Да, переменные класса используются совместно с классом и метаклассом. Они также являются общими для всех подклассов (и их метаклассов). Переменная класса обычно пишется с заглавной буквы, чтобы лучше передать идею совместного использования в более широкой области, чем класс. Вы определяете переменные класса в классе (а не в метаклассе).

Переменные класса не следует путать с переменными экземпляра класса, которые представляют собой переменные экземпляра, определенные на уровне метакласса, т. Е. Переменные экземпляра объекта класса. Это понятие несколько неясно, несмотря на его простоту (или из-за нее): переменные экземпляра всегда определяются в классе для определения формы (слотов) его экземпляров . Таким образом, если мы применим это определение к метаклассу, который является классом класса, определенная здесь переменная экземпляра определяет форму его экземпляров, из которых (обычно) только один - класс.

Возвращаясь к переменным класса, вы определяете их в классе (сторона inst) и инициализируете их в метаклассе (то есть на стороне класса). Помните, что это (частичные) глобальные объекты в том смысле, что они будут совместно использоваться экземплярами, суб-экземплярами, подклассами и метаклассами, и поэтому с ними необходимо обращаться с той же осторожностью, что и с глобальными объектами.


Еще одно уточнение

Когда мы говорим, что переменные экземпляра являются общими для экземпляров и суб-экземпляров, мы имеем в виду их имена (и позиции в памяти слотов объектов); мы не имеем в виду их значения (содержимое указанных слотов). Таким образом, два экземпляра класса Cбудут иметь colorобщее имя, скажем , если класс определяет ivar color, но их значения в каждом из экземпляров будут независимыми. Другими словами, это имя, а не значение.

В случае переменных класса общим является и имя, и значение. Фактически это Associationобъект, например Theme -> aTheme, то, что разделяется. Следовательно, любое изменение значения переменной класса влияет на все ссылки на нее. Это не относится к переменным экземпляра класса, потому что они не что иное, как переменные экземпляра, за исключением того, что они формируют класс и его подклассы, а не обычные экземпляры и субэкземпляры.


Для получения дополнительной информации о переменных Smalltalk см. https://stackoverflow.com/a/42460583/4081336

2 aka.nice Jan 20 2021 at 23:59

Как дополнение к ответу Леандро, вот основной метод реализации Squeak, который объясняет совместное использование переменных класса между стороной экземпляра (класс) и стороной класса (метакласс):

Metaclass>>classPool
    "Answer the dictionary of class variables."

    ^thisClass classPool

где thisClassуникальный экземпляр Метакласса, то есть сам класс ...

Однако есть высокие шансы найти аналогичную реализацию в большинстве диалектов Smalltalk.

Компилятор сначала попытается разрешить переменную как временный метод / блок (включая параметры метода / блока), затем переменные экземпляра, а затем общие переменные.

Метод classPool отправляется компилятором на этом последнем этапе.

Леандро объяснил, что компилятор либо разрешает привязку так же, как смещение, которое будет напрямую транскрипировано в байт-коде в случае слота переменной экземпляра или временной переменной метода, либо как своего рода ассоциация для случая общей переменной, эта ассоциация обычно добавлены в литералы CompiledMethod и эффективно используются всеми методами, работающими с этой переменной (все методы указывают на один и тот же объект Assocation, который фактически используется совместно).

Компиляторная часть гораздо более специфична для диалекта, в Squeak именно этот метод используется для разрешения привязки общих переменных:

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

Напомню, что одна из самых интересных частей Smalltalk - это то, что вы можете копаться в реализации из среды IDE, Smalltalk, по сути, написан на Smalltalk!