Является ли утверждение, что каждое создание объекта успешно выполнено в 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]]);
    

Я уверен, что есть более хорошие применения утверждений. Все вещи в умеренности...

Другие вопросы по тегам