Является ли утверждение, что каждое создание объекта успешно выполнено в Objective C?
Недавно я прочитал пример кода Apple для MVCNetworking, написанный гуру технической поддержки разработчиков Apple Куинном "Эскимос!". Пример действительно хороший опыт обучения с, я думаю, лучшие практики разработки для iOS. Что меня удивило, исходя из языков JVM, так это крайне частые утверждения:
syncDate = [NSDate date];
assert(syncDate != nil);
и это:
photosToRemove = [NSMutableSet setWithArray:knownPhotos];
assert(photosToRemove != nil);
и это:
photoIDToKnownPhotos = [NSMutableDictionary dictionary];
assert(photoIDToKnownPhotos != nil);
Это действительно необходимо? Стоит ли подражать этому стилю кодирования?
5 ответов
Если вы привыкли к Java, это может показаться странным. Вы ожидаете, что сообщение о создании объекта вызовет исключение, когда оно завершится неудачей, а не вернет nil
, Однако, хотя Objective-C в Mac OS X имеет поддержку обработки исключений; это дополнительная функция, которую можно включить / выключить с помощью флага компилятора. Стандартные библиотеки написаны так, что их можно использовать без обработки исключений: следовательно, сообщения часто возвращаются nil
чтобы указать на ошибки, а иногда требуют, чтобы вы также передали указатель на NSError*
переменная. (Это для разработки на Mac, я не уверен, сможете ли вы даже включить поддержку обработки исключений для iOS, учитывая, что вы также не можете включить сборку мусора для iOS.)
Раздел "Обработка ошибки инициализации" в документе "Язык программирования Objective-C" объясняет, как программисты Objective-C должны исправлять ошибки при инициализации / создании объекта: возвращать nil
,
Что-то вроде [NSData dataWithContentsOfFile: path]
может определенно вернуться nil
: документация по методу прямо говорит об этом. Но я, честно говоря, не уверен, что-то вроде [NSMutableArray arrayWithCapacity: n]
когда-либо возвращается nil
, Единственная ситуация, о которой я могу думать, это когда у приложения недостаточно памяти. Но в этом случае я ожидаю, что приложение будет прервано при попытке выделить больше памяти. Я не проверял это, хотя, и вполне может быть, что он возвращает nil
в этом случае. Находясь в Objective-C, вы часто можете безопасно отправлять сообщения nil
Это может привести к нежелательным результатам. Например, ваше приложение может попытаться сделать NSMutableArray
, получить nil
вместо этого, а затем счастливо продолжить отправку addObject:
в nil
и записать пустой файл на диск, а не один с элементами массива, как предполагалось. Поэтому в некоторых случаях лучше явно проверить, был ли результат сообщения nil
, Нужно ли делать это при каждом создании объекта, как это делает программист, которого вы цитируете, я не уверен. Лучше быть в безопасности, чем потом сожалеть?
Изменить: я хотел бы добавить, что при проверке, что создание объекта успешно, иногда может быть хорошей идеей, утверждая, что это может быть не лучшей идеей. Вы бы хотели, чтобы это также проверялось в выпускной версии вашего приложения, а не только в отладочной версии. Иначе это как бы лишает смысла проверять его, так как вы не хотите, чтобы конечный пользователь приложения, например, запустил пустые файлы, потому что [NSMutableArray arrayWithCapacity: n]
возвращенный nil
и приложение продолжало отправлять сообщения nil
возвращаемое значение Утверждения (с assert
или же NSAssert
) могут быть удалены из версии выпуска с флагами компилятора; Xcode, похоже, по умолчанию не включает эти флаги в конфигурацию "Release". Но если вы захотите использовать эти флаги для удаления некоторых других утверждений, вы также удалите все проверки "Создание объекта успешно завершено".
Изменить: После дальнейших размышлений, это кажется более правдоподобным, чем я думал, что [NSMutableArray arrayWithCapacity: n]
вернется nil
а не прерывать приложение, когда недостаточно памяти. Basic C malloc
также не прерывает, но возвращает NULL
указатель, когда недостаточно памяти. Но я еще не нашел четкого упоминания об этом в документации Objective-C alloc
и подобные методы.
Изменить: Выше я сказал, что я не уверен, проверяя nil
необходимо при каждом создании объекта. Но так не должно быть. Именно поэтому Objective-C позволяет отправлять сообщения nil
, которые затем возвращаются nil
(или же 0
или что-то подобное, в зависимости от определения сообщения): таким образом, nil
может распространяться через ваш код, что-то похожее на исключение, так что вам не нужно явно проверять nil
на каждое сообщение, которое может вернуть его. Но рекомендуется проверять его в тех местах, где вы не хотите, чтобы он распространялся, например, при записи файлов, взаимодействии с пользователем и т. Д., Или в случаях, когда результат отправки сообщения nil
не определено (как объяснено в документации по отправке сообщений на nil
). Я был бы склонен сказать, что это похоже на "распространение и обработку исключений" для "бедного человека", хотя не все могут согласиться с тем, что последнее лучше; но nil
ничего не говорит вам о том, почему произошла ошибка, и вы можете легко забыть проверить, где такие проверки необходимы.
Предлагаю вам прочитать эту статью о защитном программировании
Я думаю, это вопрос личного выбора. Обычно утверждения используются для целей отладки, так что приложение падает в точках подтверждения, если условия не выполняются. Вы обычно хотели бы удалить их в своих выпусках приложения все же.
Лично мне лень размещать утверждения вокруг каждого блока кода, как вы показали. Я думаю, что это слишком параноидально. Утверждения могут быть очень полезны в случае условий, в которых присутствует некоторая неопределенность.
Ага. Я думаю, что это хорошая идея. Это помогает отфильтровать крайние случаи (нехватка памяти, входные переменные пустые / ноль), как только переменные введены. Хотя я не уверен, влияние на скорость из-за накладных расходов!
Я также спрашивал об этом на Apple DevForums. По словам Куинна "Эскимос!" (автор рассматриваемого образца MVCNetworking) это вопрос стиля кодирования и его личных предпочтений:
Я использую много утверждений, потому что я ненавижу отладку. (...)
Имейте в виду, что я вырос на традиционной Mac OS, где один мошеннический указатель мог сломать всю вашу машину (аналогично программированию ядра в современных системах). В этом мире было важно найти ваши ошибки раньше, чем позже. И многие утверждения помогут вам сделать это.
Кроме того, даже сегодня я трачу большую часть своей жизни на сетевые программы. Отладка сетевых программ затруднена из-за асинхронности. Утверждают, что в этом помогают, потому что они постоянно проверяют состояние вашей программы во время ее работы.
Тем не менее, я думаю, что у вас есть правильная точка зрения с вещами вроде
+[NSDate date]
, Шансы на это возвращение ноль невелики. Утверждают, что это чисто по привычке. Но я думаю, что цена этой привычки (некоторая дополнительная печать, обучение игнорированию утверждений) мала по сравнению с выгодами.
Из этого я понимаю, что утверждение о том, что создание каждого объекта прошло успешно, не является строго необходимым.
Утверждения могут быть полезны для документирования предварительных условий в методах, в процессе разработки, в качестве вспомогательного средства проектирования для других сопровождающих (включая будущее Я). Я лично предпочитаю альтернативный стиль - разделять спецификацию и реализацию, используя методы TDD/BDD.
Утверждения могут использоваться для двойной проверки типов аргументов метода во время выполнения из-за динамической природы Objective C:
assert([response isKindOfClass:[NSHTTPURLResponse class]]);
Я уверен, что есть более хорошие применения утверждений. Все вещи в умеренности...