注意:以下是依赖于实现的。 ANSI Smalltalk Standard 定义:
未指定相同文字的值是相同还是不同的对象。特定文字的单独评估的值是相同还是不同的对象也未指定。
也就是说,您不能依赖两个(相等的)文字相同或不同。但是,下面是一个常见的实现
Squeak 和 Pharo 中的文字数组
至少在 Squeak 和 Pharo 中,文字数组是在保存(= 编译)方法时构造的,并存储在 内部 方法对象(CompiledMethod)。这意味着更改文字数组会更改存储在方法对象中的值。例如:
MyClass>>example1
| literalArray |
literalArray := #( true ).
literalArray first ifTrue: [
literalArray at: 1 put: false.
^ 1].
^ 2
此方法仅在第一次调用时返回 1:
| o p |
o := MyClass new.
o example1. "==> 1"
o example1. "==> 2"
o example1. "==> 2"
p := MyClass new.
p example1. "==> 2"
这甚至与接收者无关。
但同样,你不能依赖它,在其他 Smalltalks 中可能会有所不同。
不同的方法
-
复制(始终安全)
为了克服这个问题,您可以在使用前简单地复制文字数组。你的例子:
MyClass>>incrementedNumbers
| numbers |
numbers := #( 1 2 3 4 5 6 7 8 ) copy. "<====== "
1 to: numbers size: do: [:index |
numbers at: index put: (numbers at: index) + 1].
^ numbers
这始终是安全的,不会改变方法对象中的数组。
-
支撑阵列(主要是便携式)
虽然标准中没有定义,但大多数实现都支持这样的花括号数组表达式:
{ 1 . 'foo' . 2 + 3 }.
相当于:
Array with: 1 with: 'foo' with: 2 + 3.
这些数组是在执行时构造的(与文字数组相反),因此可以安全使用。又是你的例子:
MyClass>>incrementedNumbers
| numbers |
numbers := { 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 }. "<====== "
1 to: numbers size: do: [:index |
numbers at: index put: (numbers at: index) + 1].
^ numbers
(Ab)使用文字数组
有时有理由实际改变字面量数组(或者更一般地说,任何方法字面量,坦率地说)。例如,如果您有静态信息,如图像或二进制数据,它们根本不会改变但并不总是被使用,但您不能(无论出于何种原因)使用实例或类变量,您可能首次使用时将对象存储在文字数组中:
MyClass>>staticInformation
| holder |
holder := #( nil ).
holder first ifNil: [ holder at: 1 put: self generateBinaryData ].
^ holder first
ifNil: 检查只会在第一次执行该方法时为真,后续执行将只返回第一次调用期间self generateBinaryData 返回的值。
这种模式被一些框架使用了一段时间。
但是,特别是对于二进制数据,大多数 Smalltalks(包括 Squeak 和 Pharo)现在都支持 #[ … ] 形式的 字面字节数组。该方法可以简单地写成
MyClass>>staticInformation
^ #[42 22 4 33 4 33 11 4 33 0 0 0 0
4 33 18 4 33 4 33 9 0 14 4 33 4
33 7 4 33 0 0 9 0 7 0 0 4 33 10
4 33 4 33 7 4 33 0 0 9 0 7 0 0 4
" ... "
33 10 4 33 4 33 17 0 11 0 0 4 33
4 33 0 0 17 0 7 0 0 4 33 13 0]