Опасно ли сравнивать BOOL с YES? - PullRequest
7 голосов
/ 20 декабря 2009

Сегодня я нашел комментарий в исходном файле:

//  - no longer compare BOOL against YES (dangerous!)

Действительно ли опасно сравнение BOOL с YES в Objective-C? И почему так?

Может ли значение YES измениться во время выполнения? Может быть NO всегда 0, но YES может быть 1, 2 или 3 - в зависимости от времени выполнения, компилятора, ваших связанных структур?

Ответы [ 4 ]

17 голосов
/ 20 декабря 2009

Проблема в том, что BOOL - это не нативный тип, а typedef:

typedef signed char      BOOL;

#define YES             (BOOL)1
#define NO              (BOOL)0

Как символ, его значения не ограничены TRUE и FALSE. Что происходит с другим значением?

BOOL b = 42;
if (b)
{
    // true
}
if (b != YES)
{
    // also true
}
11 голосов
/ 20 декабря 2009

Вы никогда не должны сравнивать логические значения с что-либо ни на одном из языков на основе Си. Правильный способ сделать это - использовать:

if (b)

или

if (!b)

Это делает ваш код намного более читабельным (особенно если вы используете интеллектуально названные переменные и функции, такие как isPrime(n) или childThreadHasFinished) и безопасный. Причина что-то вроде:

if (b == TRUE)

не настолько безопасен, так как на самом деле существует большое количество значений b, которые оценятся в true, и TRUE является только одним из них.

Примите во внимание следующее:

#define FALSE 0
#define TRUE  1

int flag = 7;
if (flag)         printf ("number 1\n");
if (flag == TRUE) printf ("number 2\n");

Вы должны вывести обе эти строки распечатаны, если они работают, как ожидалось, но вы получите только первую. Это потому, что 7 на самом деле истинно, если обрабатывается правильно (0 ложно, все остальное верно), но явный тест на равенство оценивается как ложное.

Обновление:

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

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

A условие должно быть либо сравнением объектов, либо флагом (включая логические возвращаемые значения):

if (a == b) ...
if (c > d) ...
if (strcmp (e, "Urk") == 0) ...
if (isFinished) ...
if (userPressedEsc (ch)) ...

Если вы используете (что я считаю) мерзость вроде:

if (isFinished == TRUE) ...

где вы останавливаетесь:

if (isFinished == TRUE) ...
if ((isFinished == TRUE) == TRUE) ...
if (((isFinished == TRUE) == TRUE) == TRUE) ...

и т. Д.

Правильный способ сделать это для удобочитаемости - просто использовать переменные флагов с соответствующими именами.

2 голосов
/ 05 сентября 2014

Все это верно, но есть допустимые встречные аргументы, которые можно учитывать:

- Возможно, мы хотим проверить, что BOOL на самом деле ДА или НЕТ. Действительно, хранить любое другое значение, кроме 0 или 1, в BOOL довольно неправильно. Если это произойдет, не является ли это более вероятным из-за ошибки где-то еще в кодовой базе, и не является ли проверка явно против ДА просто маскировкой этой ошибки? Я думаю, что это более вероятно, чем небрежный программист, использующий BOOL нестандартным способом. Итак, я думаю, что я бы захотел бы , чтобы мои тесты не прошли, если моя БУЛА не ДА, когда я ищу правду.

- Я не обязательно согласен с тем, что «if (isWhothing)» более читабельно, особенно при оценке длинных, но в остальном читаемых вызовов функций,

например. сравнить

if ([myObj doThisBigThingWithName:@"Name" andDate:[NSDate now]]) {}

с:

if (![myObj doThisBigThingWithName:@"Name" andDate:[NSDate now]]) {}

Первое сравнивается с истинным, второе с ложным, и трудно отличить при быстром чтении кода, верно?

Сравните это с:

if ([myObj doThisBigThingWithName:@"Name" andDate:[NSDate now]] == YES) {}

и

if ([myObj doThisBigThingWithName:@"Name" andDate:[NSDate now]] == NO) {}

... и не намного ли читабельнее?

Опять же, я не говорю, что один путь правильный, а другой неправильный, но есть некоторые контрапункты.

0 голосов
/ 20 декабря 2009

Когда в коде используется переменная BOOL, предполагается, что она будет использоваться в качестве логического значения. Компилятор не проверяет, получает ли переменная BOOL другое значение, точно так же, как компилятор не проверяет, инициализируется ли переменная, переданная методу, со значением, взятым между набором констант.

...