Почему NSArray arrayWithObjects требует завершающий ноль? - PullRequest
15 голосов
/ 21 августа 2009

Я понимаю, что это отмечает конец набора varargs, но почему он не может быть реализован таким способом, который не требует nil?

Ответы [ 6 ]

17 голосов
/ 21 августа 2009

Все это связано с тем, что C вызывает ABI.

Рассмотрим эти методы:

- (id)initWithFormat:(NSString *)format, ...;
+ (id)arrayWithObjects:(id)firstObj, ...;
+ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;

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

Теперь рассмотрим три метода. Все три имеют совершенно разные требования к тому, что может присутствовать в списке переменных аргументов. Массив должен быть группой объектов, за которыми следует ноль. Словарь требует несколько пар объектов, за которыми следует ноль. Наконец, строковому методу требуется набор аргументов, соответствующих типам в строке формата.

Все эти поведения напрямую связаны с вызываемым методом, и, если автор API решит пойти на «сложный в использовании», поведение декодирования переменных аргументов может быть изменено во время выполнения, просто чтобы сделать жизнь трудно.

Итог: C ABI не имеет синтаксиса, который позволяет указывать, что метод или функция принимает переменное число аргументов с любым набором ограничений на аргументы или их завершение.

Objective-C может изменить правила только для объявлений и вызовов методов, но это не поможет с функциями C или C ++, оба из которых Objective-C должны оставаться совместимыми.

3 голосов
/ 21 августа 2009

Любая функция varargs должна каким-то образом знать, сколько параметров присутствует - если вы не завершите nil, вам просто нужно что-то еще. В этом случае очевидной альтернативой является длина, но тогда вам нужно будет обновлять аргумент длины каждый раз, когда вы меняете количество элементов в массиве, и это может быть громоздким или, что еще хуже, сломанным.

Я предполагаю, что у вас есть массив, который может содержать ноль?

2 голосов
/ 26 августа 2010

Существуют различные обходные пути, но мой любимый в настоящее время больше подходит для приложений, чем для выпущенных фреймворков. Примите NSArray в вашем методе вместо «...», а затем заполните его вспомогательным макросом, который находится ниже в вашем файле префикса или заголовке утилиты.

# define $ array (objs ...) [NSArray arrayWithObjects: objs, nil]

Это позволит использовать несколько хорошо помеченных аргументов переменной длины, и вы избавите себя от архаичной схемы необходимости использовать первый аргумент, а затем va_list и его братьев в пользу цикла for-in или множества других коллекций. инструменты доступны.

[self findMatchingSetAndAlert: @ "title" связей: $ array (tie1, tie2, tie3) рубашки: $ array (рубашка1, рубашка2, рубашка3, рубашка4)];

Если кто-то знает, как реализовать список без разделителей, такой как stringWithFormat, пожалуйста, сообщите нам об этом! Он использует атрибуты и макросы или что-то, разработанное специально для форматирования, но они реализованы каким-то образом.

2 голосов
/ 21 августа 2009

@ bbum дает некоторые технические детали. Вот некоторые мысли о практическом «почему они не исправляют это?»

Помните: C - просто ассемблер, а Obj-C - просто C ... Конечно, его можно переработать, но на это почти нет давления. Вы все еще не можете поместить nil в массив (это потребует огромных изменений). Теперь компилятор предупреждает вас, если вы забыли ноль, так что разработчики не часто жалуются на это. Компромисс делает язык намного (намного!) Более простым, чем его род, получая преимущества десятилетий оптимизации компилятора C и гарантированной совместимости с кодом C.

Одна вещь, которая должна стать понятной из обсуждения @ bbum: NSArray не является языковой особенностью Objective-C. C-массивы - это языковая функция, но NSArrays - это просто другой объект, не отличающийся от объектов, которые вы пишете.

1 голос
/ 05 сентября 2012

Теперь вы можете использовать новые литералы коллекций (псевдонимы контейнеров) в Objective-C. Смотри http://clang.llvm.org/docs/ObjectiveCLiterals.html

0 голосов
/ 31 января 2019

Простая причина в том, что за кулисами находится цикл for, который будет продолжать принимать аргументы от va_list до достижения nil. Таким образом, конечным условием могло быть что угодно, например, строка «стоп». Но nil на самом деле довольно умный.

Допустим, у нас есть три объекта hansel, gretel и woodcutter и мы создаем их массив:

NSArray *startCharacters = [NSArray arrayWithObjects:hansel, gretel, woodcutter, nil];

Теперь мы понимаем, что woodcutter никогда не был инициирован, поэтому он nil, но startCharacters будет по-прежнему создаваться с объектами hansel и gretel, поскольку, когда он достигает woodcutter, он завершается. Таким образом, нулевое окончание в arrayWithObjects: предотвращает сбой приложения.

Если вам не нравится выписывать nil, вы всегда можете создать массив следующим образом:

NSArray *startCharacters = @[hansel, gretel, woodcutter];

Это верно, оно короче, но оно потерпит крах, если объект будет nil. Таким образом, вывод заключается в том, что arrayWithObjects: все еще может быть очень полезным, и вы можете использовать его в своих интересах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...