Каковы утверждения или практические рекомендации NSAssert?
Интересно, хорошая ли привычка использовать NSAssert повсюду? Какая польза от этого? В каких ситуациях это хорошая идея использовать его?
Ответы
Ответ 1
Широкое использование NSAssert()
не превратит ObjC в Eiffel, но это все еще довольно хорошая практика, если вы помните, как это реализовано и что оно делает. Что нужно помнить о NSAssert()
:
Xcode не отключает NSAssert()
в режиме деблокирования по умолчанию. Не забудьте добавить NS_BLOCK_ASSERTIONS
в GCC_PREPROCESSOR_DEFINITIONS
в xcconfig. (Вы с помощью xcconfigs, правда?). Общей проблемой является утверждение non-nil в случаях, когда nil будет работать тихо; это может означать крах полетов для вещей, которые могли бы изящно восстановиться. Это не связано с макросом NDEBUG
, используемым assert()
, и вы должны помнить, что нужно определить оба варианта, если ваш код включает оба типа утверждений.
Если вы скомпилируете NSAssert()
в режиме Release, вы не получите никаких указаний, где возникла проблема, когда клиенты отправляют вам свои журналы. Я лично переношу NSAssert()
в свои собственные макросы, которые всегда регистрируются, даже в режиме Release.
NSAssert()
часто приводит к дублированию логики. Рассмотрим случай тестирования указателя С++ для NULL. Вы используете NSAssert()
, но вам все равно нужно использовать простой тест if()
, чтобы избежать сбоев в работе. Такой дублированный код может стать источником ошибок, и я видел, что код не работает из-за утверждений, которые более недействительны. (К счастью, это обычно в режиме отладки!) Я много обсуждал, как создать макрос, который объединил бы утверждение и if()
, но это сложно сделать, не будучи хрупким или затруднять понимание кода.
Из-за последней проблемы я обычно помещаю "NSAssert(NO, ...)
" в предложение else{}
, а не выполняет утверждение в верхней части метода. Это не идеально, потому что он переносит контракт от сигнатуры метода (тем самым уменьшая его документальное преимущество), но это наиболее эффективный подход, который я нашел на практике.
Ответ 2
Основным предупреждением являются побочные эффекты. Если вы пишете:
NSAssert([self.navigationController popViewControllerAnimated:YES]!=nil,@"Something fails");
popViewControllerAnimated:
будет выполняться в версии отладки, но не в версии выпуска, которая разбивает NSAssert()
. Это означает, что ваша версия релиза будет отличаться от версии отладки.
Эта проблема исчезает, если вы достаточно осторожны:
UIViewController* vc = [self.navigationController popViewControllerAnimated:YES];
NSAssert(vc!=nil,@"Something fails");
Ответ 3
Debugging. Всякий раз, когда вы пишете код, вы почти всегда делаете предположения. Предположения о состоянии окружающей среды, значения ваших параметров, локальные переменные и поля и т.д. Часто эти предположения ошибочны (старый коллеж дал мне хорошую оценку, что "Успение является матерью всех fsckups" ).
Существуют утверждения, подтверждающие ваши предположения, в тот момент, когда вы их делаете. У вас есть метод
void foo(int x)
{
…
}
что вы знаете и задокументировали только работы для x > 5? Подтвердите это!
Утверждения живут вместе с модульным тестированием и формальными методами как часть хорошей практики кодирования. Хотя некоторые могут подумать, что всеобъемлющие модульные тесты гарантируют, что утверждения излишни, это не так. Например, у вас может быть предположение о методе, который, например, сотрудникам всегда старше 16 лет и менее 100 лет - но этот код на данный момент не требует этого. Единичные тесты, которые передают эти параметры, будут успешными, но позже, когда вам нужно будет использовать ваше предположение, у вас будет код везде, который прошел тесты, но это неправильно.
Ответ 4
Я настоятельно рекомендую эту статью
http://www.mikeash.com/pyblog/friday-qa-2013-05-03-proper-use-of-asserts.html
И, как говорится в статье, часто бывает полезно использовать assert-механизм, который не является NSAssert. Например:
https://github.com/hfossli/AGAssert