Весь этот ответ находится в контексте соответствия спецификации, а не того, что движок делает в определенный момент или исторически.
. Фактический вопрос очень расплывчатый.
будут ли свойства в том же порядке, что я добавил их
В каком контексте?
Ответ: это зависит от ряда факторов. В общем, no .
Иногда да
Здесь вы можете рассчитывать на порядок ключей свойств для plain
Objects
:
- Соответствующий ES2015 двигатель
- Собственные свойства
Object.getOwnPropertyNames()
,Reflect.ownKeys()
,Object.getOwnPropertySymbols(O)
Во всех случаях эти методы включают неперечислимые ключи свойств и ключи заказа, указанные в
[[OwnPropertyKeys]]
(см. ниже). Они различаются по типу ключевых значений, которые они включают (String
и / илиSymbol
). В этом контекстеString
включает целые значения.
Object.getOwnPropertyNames(O)
Возвращает собственные свойства
String
O
] ).
Reflect.ownKeys(O)
Возвращает
O
собственныеString
- иSymbol
клавиши свойства.
Object.getOwnPropertySymbols(O)
Возвращает собственные свойства
Symbol
O
.
[[OwnPropertyKeys]]
Порядок по существу: целочисленный
Strings
в порядке возрастания, нецелоподобныйStrings
в порядке создания, Символы в порядке создания. В зависимости от того, какая функция вызывает это, некоторые из этих типов могут не включаться.Специфическим языком является то, что ключи возвращаются в следующем порядке:
- .. каждый собственный ключ свойства
P
O
[объект, который повторяется], который является целым индексом, в порядке возрастания числового индекса- ... каждый собственный ключ свойства
P
изO
который является строкой, но не является целым индексом, в порядке создания свойства- ... каждый собственный ключ свойства
P
изO
, который является символом, в порядке создания свойства
Map
Если вас интересуют упорядоченные карты, вы должны рассмотреть возможность использования типа
Map
, введенного в ES2015, вместо простогоObjects
.
Ваше нулевое завершение в конце списка аргументов передается вашей функции как int
(32-битное) значение, но ваш va_arg
вытягивает значение NSInteger
(он же long
) из стека. Таким образом, вы извлекаете дополнительные 32 бита из стека и смотрите на все это как на одно 64-битное значение, половина которого - это ноль, который вы намеревались, а половина - то, что произошло рядом с ним в памяти. в то время.
Вы должны сделать это, чтобы получить желаемое поведение:
[var test:Enum1, Enum2, Enum3, Enum4, Enum5, Enum6, (NSInteger)0];
Как поясняет Роб Мейофф в комментариях ниже, литерал с нулевым нулем, являющийся целочисленным значением, рассматривается как [115 ]. Применяются обычные целочисленные правила продвижения C *; в списке varargs, поскольку аргументы не имеют объявленного типа, более целочисленные типы повышаются до - и передаются как - int
с. Поскольку компилятор не имеет представления о фактических типах, которые вы ожидаете увидеть во время выполнения, этот int
не будет повышен до long
для вас, поэтому он попадает в стек как int
.
Как правило, завершение varargs выполняется либо неявно (предвидение стиля printf
числа и типов arg), либо с константой, подобной nil
, которая будет правильной ширины, и эти подходы позволяют избежать этой проблемы. В старом 32-битном мире, где значения enum были int
с, и NSInteger
были int
, и целочисленное продвижение по умолчанию было , также - int
, эти различия были скрыты.
На практике это может означать для вашего дизайна кода то, что вы, возможно, зарезервировали бы специальное значение перечисления (не обязательно ноль), чтобы использовать его в качестве ограничителя списка, чтобы гарантировать, что он имеет правильный тип. Или вы, возможно, измените, добавив число аргументов к вызову функции.
* См. Стандарт C18 §6.3.1.1 ;-)
Объяснитель бонуса: почему вы увидели значение enum == 4294967296
?
0x0000000100000000
. Вторая (нижняя) половина этого значения была 32-битным нулем, который вы поставили туда. Верхняя половина выглядит так, как будто это просто число 1
. Сначала я предполагал, что это будет какая-то другая (действительная) часть текущего стекового фрейма, но некоторые исследования (на 64-битном Mac, использующем текущий llvm с Xcode и т. Д.) Показывают, что компилятор устанавливает вызов -test:...
путем нажатия на enum args с помощью movq
(move quad = 64bit) и конечного нулевого arg с movl
(move long = 32bit). Поскольку стек выровнен на 64 бита, в стеке памяти между предпоследним и конечным аргументами остается «дыра» в 32 бита (т.е. не перезаписывается). Это место, которое содержало 0x1
. Таким образом, вы не читаете соседний аргумент или другое значение, которое является допустимым, но используется для чего-то другого. Вы читаете призрачное значение из рабочего пространства какого-то более раннего вызова функции. В моей отладке это не похоже на alloc / init из Variable
- это что-то предшествующее и не связанное с тестовым кодом под рукой.