Каковы утверждения или практические рекомендации 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 лет - но этот код на данный момент не требует этого. Единичные тесты, которые передают эти параметры, будут успешными, но позже, когда вам нужно будет использовать ваше предположение, у вас будет код везде, который прошел тесты, но это неправильно.