Утверждается, что в Objective C необходимо создание каждого объекта?

Недавно я прочитал образец кода Apple для MVCNetworking, написанный гуру технической поддержки Apple Developer Quinn "The Eskimo!". Образец действительно хороший опыт обучения с тем, что я думаю, лучшие методы развития для развития iOS. Меня удивили, исходя из языков JVM, очень частые утверждения вроде этого:

syncDate = [NSDate date];
assert(syncDate != nil);

и это:

photosToRemove = [NSMutableSet setWithArray:knownPhotos];
assert(photosToRemove != nil);

и это:

photoIDToKnownPhotos = [NSMutableDictionary dictionary];
assert(photoIDToKnownPhotos != nil);

Это действительно необходимо? Этот стиль кодирования стоит подражать?

Ответ 1

Если вы привыкли к Java, это может показаться странным. Вы ожидали бы, что сообщение создания объекта должно было бы генерировать исключение, если оно терпит неудачу, вместо возврата nil. Тем не менее, в то время как Objective-C в Mac OS X поддерживается обработка исключений; это дополнительная функция, которая может быть включена/выключена с помощью флага компилятора. Стандартные библиотеки написаны так, что они могут использоваться без обработки исключений: поэтому сообщения часто возвращают nil, чтобы указывать на ошибки, а иногда также требуют, чтобы вы также передавали указатель на переменную NSError*. (Это для разработки Mac, я не уверен, можно ли включить поддержку обработки исключений для iOS, учитывая, что вы также не можете включить сбор мусора для iOS.)

Раздел "Обработка отказа инициализации" в документе "Язык программирования Objective-C" объясняет, как должны работать программисты Objective-C с ошибками в инициализации/создании объекта: то есть return 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 является undefined (как описано в документации по отправке сообщений на nil). Я был бы склонен сказать, что это похоже на "версию бедствия" для распространения и обработки исключений, хотя не все могут согласиться с тем, что последнее лучше; но nil ничего не говорит о том, почему произошла ошибка, и вы можете легко забыть проверить, где такие проверки необходимы.

Ответ 3

Угу. Я думаю, что это хорошая идея. Это помогает отфильтровать случаи краев (вне памяти, входные переменные empty/no), как только будут введены переменные. Хотя я не уверен, что это связано с быстротой из-за накладных расходов!

Ответ 4

Я думаю, это вопрос личного выбора. Обычно утверждения используются для целей отладки, так что приложение падает в точках утверждения, если условия не выполняются. Обычно вы хотели бы раздеть их в своих версиях приложений.

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

Ответ 5

У меня также спросил об этом в Apple DevForums. По словам Куинна "Эскимосский!" (автор рассматриваемого образца MVCNetworking), это вопрос стиля кодирования и его личные предпочтения:

Я использую множество утверждений, потому что ненавижу отладку. (...)

Имейте в виду, что я вырос с традиционной Mac OS, где один указатель на изгнанник может сбить всю вашу машину (аналогично программированию ядра на текущих системах). В этом мире было важно найти ваши ошибки раньше, чем позже. И много подтверждает.

Кроме того, даже сегодня я большую часть своей жизни занимаюсь сетевыми программами. Отладка сетевых программ затруднена из-за вовлеченной асинхронности. Помогает с этим, потому что они постоянно проверяют состояние вашей программы по мере ее запуска.

Однако, я думаю, что у вас есть действительная точка с такими вещами, как +[NSDate date]. Шансы на то, что возвращающийся ноль низкий. Утверждение существует только по привычке. Но я думаю, что затраты на эту привычку (некоторые дополнительные набрав, учась игнорировать утверждения) являются небольшими по сравнению с преимуществами.

Из этого я понимаю, что утверждение о том, что каждое создание объекта преуспело, не является строго необходимым.

  • Ассессоры могут быть ценными для документировать предварительные условия в методах, во время разработки, в качестве вспомогательного средства проектирования для других сопровождающих (включая будущее я). Я лично предпочитаю альтернативный стиль - отделять спецификацию и реализацию с использованием методов TDD/BDD.

  • Ассески могут использоваться для двойного проверки типов времени выполнения аргументов метода из-за динамической природы Objective C:

    assert([response isKindOfClass:[NSHTTPURLResponse class]]);
    

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