Где в Smalltalk определены переменные класса?
Мне было интересно, если я определю новую переменную класса, например, для класса MyClass
, определение будет внутри MyClass
или внутри MyClass class
? Знает ли MyClass class
вообще о новой переменной класса?
Ответы
Да, переменные класса используются совместно с классом и метаклассом. Они также являются общими для всех подклассов (и их метаклассов). Переменная класса обычно пишется с заглавной буквы, чтобы лучше передать идею совместного использования в более широкой области, чем класс. Вы определяете переменные класса в классе (а не в метаклассе).
Переменные класса не следует путать с переменными экземпляра класса, которые представляют собой переменные экземпляра, определенные на уровне метакласса, т. Е. Переменные экземпляра объекта класса. Это понятие несколько неясно, несмотря на его простоту (или из-за нее): переменные экземпляра всегда определяются в классе для определения формы (слотов) его экземпляров . Таким образом, если мы применим это определение к метаклассу, который является классом класса, определенная здесь переменная экземпляра определяет форму его экземпляров, из которых (обычно) только один - класс.
Возвращаясь к переменным класса, вы определяете их в классе (сторона inst) и инициализируете их в метаклассе (то есть на стороне класса). Помните, что это (частичные) глобальные объекты в том смысле, что они будут совместно использоваться экземплярами, суб-экземплярами, подклассами и метаклассами, и поэтому с ними необходимо обращаться с той же осторожностью, что и с глобальными объектами.
Еще одно уточнение
Когда мы говорим, что переменные экземпляра являются общими для экземпляров и суб-экземпляров, мы имеем в виду их имена (и позиции в памяти слотов объектов); мы не имеем в виду их значения (содержимое указанных слотов). Таким образом, два экземпляра класса C
будут иметь color
общее имя, скажем , если класс определяет ivar color
, но их значения в каждом из экземпляров будут независимыми. Другими словами, это имя, а не значение.
В случае переменных класса общим является и имя, и значение. Фактически это Association
объект, например Theme -> aTheme
, то, что разделяется. Следовательно, любое изменение значения переменной класса влияет на все ссылки на нее. Это не относится к переменным экземпляра класса, потому что они не что иное, как переменные экземпляра, за исключением того, что они формируют класс и его подклассы, а не обычные экземпляры и субэкземпляры.
Для получения дополнительной информации о переменных Smalltalk см. https://stackoverflow.com/a/42460583/4081336
Как дополнение к ответу Леандро, вот основной метод реализации 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!