Следует ли мне использовать буквальный синтаксис или конструкторы для создания словарей и массивов?
Я читаю Руководство по разработке для iOS, чтобы ознакомиться с языком Objective-C, и в настоящее время у меня возникла небольшая путаница в теме литералов контейнера и нотации подписки, поскольку она касается создания таких объектов, как NSDictionary
,
Я понимаю, что есть несколько способов создания NSDictionary
объекты, включая кодировку Key-Value (dictionaryWithObjects:forKeys:
а также dictionaryWithObjectsAndKeys:
или их соответствующие инициализаторы). Ссылка на источник.
Насколько я понимаю, есть два основных способа сделать это, а затем есть другой способ, который использует контейнерные литералы, продемонстрированные здесь:
NSDictionary *myDictionary = @{
@"name" : NSUserName(),
@"date" : [NSDate date],
@"processInfo" : [NSProcessInfo processInfo]
};
Какой лучший способ использовать? Есть ли какая-то польза от использования метода Container Literal по сравнению с предыдущими двумя или это просто удобство для программистов?
У меня сложилось впечатление, что это также еще один более простой способ кодирования таких вещей, как массивы. Это правда или я что-то упускаю здесь? Являются ли эти методы просто вопросом личных предпочтений?
3 ответа
Я не согласен с другими ответами, опубликованными до сих пор: почти все время лучше использовать новый синтаксис литерала контейнера, чем использовать конструкторы. Они помогают с корректностью кода, и на самом деле беспокоиться о совместимости не так уж и много.
Правильность кода
Контейнерные литералы действительно являются синтаксическим сахаром, но, в частности, они отображаются на "безопасные" методы конструктора. +[NSArray arrayWithObjects:count:]
а также +NSDictionary dictionaryWithObjects:forKeys:count:
, Создание массива или словаря с использованием одного из этих методов напрямую не так уж и удобно, поэтому многим программистам проще arrayWithObjects:
а также dictionaryWithObjectsAndKeys:
, Однако последние методы имеют неприятную ловушку: поскольку список аргументов должен заканчиваться nil
, вы можете оказаться с неожиданным содержимым массива / словаря, если вы передадите nil
где вы собираетесь передать объект.
Например, скажем, вы устанавливаете словарь, отображающий свойства одного из объектов вашей модели (может быть, вы собираетесь отправить его как JSON?):
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
person.name, @"name", person.title, @"title", person.address, @"address",
nil];
Если этот код сталкивается с Person
для кого нет title
в результирующем словаре будет отсутствовать @"address"
ключ и его значение. Вы могли бы потратить часы на то, чтобы выяснить, почему некоторые люди в вашей базе данных пропускают адреса (и даже посмотреть код выше и вырваться, задаваясь вопросом, почему это не работает, когда- нибудь, я устанавливаю это прямо здесь!). У многих из нас есть.
Напротив, если вы используете буквальную форму, как это:
NSDictionary *dictionary = @{
@"name": person.name, @"title": person.title, @"address": person.address };
Это будет расширено до чего-то вроде этого:
id objects[] = { person.name, person.title, person.address };
id keys[] = { @"name", @"title", @"address" };
NSUInteger count = sizeof(objects) / sizeof(keys);
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects
forKeys:keys
count:count];
И если person.name
или же person.title
возвращается nil
этот метод будет генерировать исключение вместо тихого создания данных, которые вы не хотите. (В любом случае вам придется решить, как вы хотите, чтобы ваш код обрабатывал nil
названия, но таким образом вы поймете проблему раньше.) И, конечно, вы могли бы написать эту "более безопасную" форму самостоятельно, вместо того, чтобы использовать эквивалентный синтаксический сахар, но вы уверены, что не откажетесь от привычки писать dictionaryWithObjectsAndKeys:
потому что это короче?
Совместимость
Код, сгенерированный литералами контейнера (и, в этом случае, числовыми литералами и выражениями в штучной упаковке), не использует новый API, поэтому вы можете скомпилировать его с Xcode 4.4 или новее (или Clang 3.1 или новее напрямую) и развернуть в любой версии Foundation. Однако вам необходимо учитывать совместимость, если ваш исходный код будет также использоваться со старыми компиляторами или GNUStep. (Хотя, похоже, GNUStep хорош и с Clang сейчас.)
И это не является частью вопроса, но, поскольку это связано с темой: то же самое относится к "своего рода" истине для синтаксиса подписки нового объекта. При этом используются новые методы, определенные только в Mac OS X 10.6 и iOS 6.0... но эти методы предоставляются libarclite
, (Вы знаете, библиотека, которая связывается, когда вы пытаетесь развернуть код ARC обратно на iOS 4.3 или Mac OS X 10.6 - это больше не только для ARC!) Так что все, что вам нужно сделать, это объявить их в заголовке, ссылке ARCLite, если ты еще не, и ты готов идти.
Там нет "лучший способ". Используйте то, что лучше для конкретного случая использования. Например, если вы хотите, чтобы ваше приложение было переносимым (т. Е. Логика, которая требует только Foundation, а не UIKit, может работать и на других платформах, таких как Mac OS X или Linux с GNUstep и т. Д.), Тогда избегайте использования буквального синтаксиса - они Вы не очень портативны. Если вам нужно, чтобы он работал только на iOS, используйте их, потому что они удобны.
Кроме того, эти обозначения являются только синтаксическим сахаром - то есть они сопоставляются с именами методов (насколько я знаю, точно с двумя методами, которые вы упомянули в своем вопросе), поэтому они не влияют на производительность, поведение алгоритм и т. д.
И да, вы догадались, это верно: то же самое относится к новому синтаксису подписки - для NSArray он вызывает - objectAtSubscriptedIndex:
,
Вы можете использовать их в GNU/Linux с GNUstep и clang. В большинстве моих случаев GNUstep работает с clang намного лучше, чем все версии gcc. (Извините, я должен просто отредактировать другой ответ, я новичок в этом)