Цель-C: BOOL против bool

192

Я видел "новый тип" BOOL( YES,NO ).

Я читал, что этот тип почти как символ.

Для тестирования я сделал:

NSLog(@"Size of BOOL %d", sizeof(BOOL));
NSLog(@"Size of bool %d", sizeof(bool));

Приятно видеть, что оба журнала отображают «1» (иногда в C ++ bool имеет тип int, а его размер равен 4)

Так что мне просто интересно, были ли какие-то проблемы с типом bool или что-то в этом роде?

Могу ли я просто использовать bool (который, кажется, работает) без потери скорости?

Francescu
источник

Ответы:

199

Из определения в objc.h:

#if (TARGET_OS_IPHONE && __LP64__)  ||  TARGET_OS_WATCH
typedef bool BOOL;
#else
typedef signed char BOOL; 
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
// even if -funsigned-char is used.
#endif

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

Итак, да, вы можете предположить, что BOOL - это символ. Вы можете использовать boolтип (C99) , но все платформы Apple Objective-C и большая часть кода Objective-C / Cocoa используют BOOL, поэтому вы избавите себя от головной боли, если typedef когда-либо изменится, просто используя BOOL.

Барри Уарк
источник
17
«все рамки Apple» - не соответствует действительности. Взгляните на CGGeometry.h, а именно: CG_INLINE bool __CGPointEqualToPoint (CGPoint point1, CGPoint point2) {return point1.x == point2.x && point1.y == point2.y; }
Эллиот
58
@ Эллиот Вы правы. Многие из фреймворков C (CoreFoundation, CoreGraphics и т. Д.) Используют C99 bool. Все фреймворки Objective-C используют BOOL.
Барри Уарк
@ Cœur вы отредактировали определение образца кода BOOL, но текст ниже остался прежним. Это немного сбивает с толку и неправильно. Смотри мой ответ.
любопытно
Чтобы узнать другое поведение, посмотрите на ниже. NSInteger progressTime = 2;//any value NSInteger totalTime = 1;//any value BOOL success = (progressTime>=totalTime)// это всегда дает NO Но как только я получил это (progressTime>=totalTime)значение в boolтип, successон возвращает правильный результат. Я не понимаю это поведение. Я использую Xcode 7.xи iOSверсия была 8.x. @BarryWark
Камар Шад
34

Как уже упоминалось выше, BOOL является подписанным символом. bool - тип из стандарта C99 (int).

BOOL - ДА / НЕТ. bool - правда / ложь.

Смотрите примеры:

bool b1 = 2;
if (b1) printf("REAL b1 \n");
if (b1 != true) printf("NOT REAL b1 \n");

BOOL b2 = 2;
if (b2) printf("REAL b2 \n");
if (b2 != YES) printf("NOT REAL b2 \n");

И результат

НАСТОЯЩИЙ b1
НАСТОЯЩИЙ b2
НЕ НАСТОЯЩИЙ b2

Обратите внимание, что bool! = BOOL. Результат ниже только один раз - реальный b2

b2 = b1;
if (b2) printf("ONCE AGAIN - REAL b2 \n");
if (b2 != true) printf("ONCE AGAIN - NOT REAL b2 \n");

Если вы хотите конвертировать bool в BOOL, используйте следующий код

BOOL b22 = b1 ? YES : NO; //and back - bool b11 = b2 ? true : false;

Итак, в нашем случае:

BOOL b22 = b1 ? 2 : NO;
if (b22)    printf("ONCE AGAIN MORE - REAL b22 \n");
if (b22 != YES) printf("ONCE AGAIN MORE- NOT REAL b22 \n");

И так .. что мы получаем сейчас? :-)

бериллий
источник
3
Вы могли бы вместо использования троичного оператора использовать !!b1. Для преобразования между ними
Ричард Дж. Росс III
1
«НЕ РЕАЛЬНО b2» не напечатано на моем симуляторе iPhone SE.
Игрок
12

На момент написания статьи это самая последняя версия objc.h:

/// Type to represent a boolean value.
#if (TARGET_OS_IPHONE && __LP64__)  ||  TARGET_OS_WATCH
#define OBJC_BOOL_IS_BOOL 1
typedef bool BOOL;
#else
#define OBJC_BOOL_IS_CHAR 1
typedef signed char BOOL; 
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
// even if -funsigned-char is used.
#endif

Это означает, что на 64-битных устройствах iOS и на WatchOS BOOLэто то же самое, что boolи на всех других устройствах (OS X, 32-битная iOS) это signed charи даже не может быть переопределено флагом компилятора-funsigned-char

Это также означает, что этот пример кода будет работать по-разному на разных платформах (протестировал сам):

int myValue = 256;
BOOL myBool = myValue;
if (myBool) {
    printf("i'm 64-bit iOS");
} else {
    printf("i'm 32-bit iOS");
}

КСТАТИ не присваивайте вещи , как array.countк BOOLпеременной , потому что около 0,4% от возможных значений будет отрицательным.

Петр Тобольский
источник
компилятор выдаст ошибку, если вы используете bool в качестве параметра блока, который определен для получения BOOL (например, анимационные блоки UIView), когда вы компилируете для iOS 32 бита (iPhone 5C ...). Я использую C ++ bool везде в моем коде и BOOL в API, которые определены с помощью BOOL
stephane k.
8

Objective-C типа вы должны использовать это BOOL. Нет ничего похожего на родной логический тип данных, поэтому, чтобы быть уверенным, что код компилируется всеми используемыми компиляторами BOOL. (Это определено в Apple-Frameworks.

Георг Шолли
источник
2
Это не совсем точно. BOOLопределяется языком Objective-C (он находится в одном из objc/*.hзаголовков), а не фреймворками. Кроме того, при компиляции с C99 (который я думаю по умолчанию), тогда есть родной логический тип _Bool(или, boolесли stdbool.hвключен).
Dreamlax
5

Да, BOOL - это typedef для подписанного символа в соответствии с objc.h.

Я не знаю о буле, хотя. Это вещь C ++, верно? Если он определен как символ со знаком, где 1 - YES / true, а 0 - NO / false, тогда я думаю, что не имеет значения, какой из них вы используете.

Поскольку BOOL является частью Objective-C, тем не менее, для ясности, вероятно, имеет смысл использовать BOOL (другие разработчики Objective-C могут быть озадачены, если увидят использование bool).

Джефф
источник
6
_Bool определен в C99, и в стандартном заголовке stdbool.h определен макрос bool (который расширяется до _Bool), и здесь также определены true / false.
Брайан Митчелл
4

Другое различие между bool и BOOL заключается в том, что они не преобразуются точно в один и тот же тип объектов, когда вы выполняете наблюдение значения ключа или когда вы используете такие методы, как - [NSObject valueForKey:].

Как все говорили здесь, BOOL - это символ. Как таковой, он преобразуется в NSNumber с символом. Этот объект неотличим от NSNumber, созданного из обычного символа, такого как «A» или «\ 0». Вы полностью потеряли информацию о том, что у вас изначально была БУЛА.

Однако bool преобразуется в CFBoolean, который ведет себя так же, как NSNumber, но сохраняет логическое происхождение объекта.

Я не думаю, что это аргумент в дебатах BOOL vs. bool, но это может укусить вас однажды.

Вообще говоря, вы должны использовать BOOL, поскольку этот тип используется везде в API-интерфейсах Cocoa / iOS (разработан до C99 и его родного типа bool).

Гвендал Руэ
источник
2

Принятый ответ был отредактирован, и его объяснение стало немного неправильным. Образец кода был обновлен, но текст ниже остался прежним. Вы не можете предполагать, что BOOL является символом на данный момент, поскольку он зависит от архитектуры и платформы. Таким образом, если вы запустите свой код на 32-битной платформе (например, iPhone 5) и напечатаете @encode (BOOL), вы увидите «c». Это соответствует типу символа . Но если вы запустите свой код на iPhone 5s (64 бит), вы увидите «B». Это соответствует типу bool .

любопытный
источник
1

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

  1. Когда я увижу базовый тип в вашем источнике, я сразу пойму это. Если это typedef, я должен посмотреть его, чтобы увидеть, с чем я действительно имею дело.
  2. При портировании на другой компилятор или добавлении другой библиотеки их набор typedefs может конфликтовать и вызывать проблемы, которые трудно отладить. Я только что закончил с этим. В одной библиотеке логический тип был определен как int, а в mingw / gcc он был определен как тип char.
сойка
источник
4
Ну ... можно ожидать, что вы знаете стандартные определения типов вашего языка (подумайте size_t), и оба bool(C99) и BOOL(ObjC) попадают в эту категорию. И если ваш код потерпел неудачу из-за изменения typedef, виноват ваш код, поскольку вы, очевидно, не рассматривали typedef как непрозрачную вещь, а полагались на его реализацию на одной платформе. (Нечего стыдиться, это случается, но это не typedef, чтобы обвинить.)
DevSolar
1
«Стандартные» определения типов не кажутся очень стандартными (например, какое-то время MS не поддерживала стандарты posix и т. Д.). Если вы не используете typedefs, то проблема с изменением typedefs или его различием в разных компиляторах устраняется.
Джей
1
-1, typedefs в целом служат двум важным целям (среди прочих): обеспечивают хорошую семантику и некоторые неверные направления. В идеале вам не нужно знать базовый тип, на который ссылается typedef, к сожалению, эта система не идеальна, и иногда вам нужно знать. Моя точка зрения такова: вы должны следовать соглашению, потому что даже признав, что оно не идеально, оно лучше альтернативы.
Жоао Портела
2
@Jay: Извините, я должен был объяснить, почему это "неправильное направление" хорошо. Я попытаюсь привести пример: если вы используете логическое значение typedef вместо логического типа int или char напрямую, вы разрешаете использовать разные типы (которые все еще работают) на каждой платформе без нарушения кода [причины это может отличаться, но мы можем представить платформу, в которой символ может быть смещен в памяти и, следовательно, медленнее, поэтому вместо логического значения int можно использовать логическое значение].
Жоао Портела
2
@Jay: Под «хорошей семантикой» я подразумеваю, что когда вы объявляете логическое значение BOOL varnameвместо логического, char varnameстановится более очевидным, что два допустимых значения для этой переменной - true/ YESили false/ NO.
Жоао Портела
1

Как уже упоминалось выше, это BOOLможет быть unsigned charтип в зависимости от вашей архитектуры, в то время как boolимеет тип int. Простой эксперимент покажет разницу, почему BOOL и bool могут вести себя по-разному:

bool ansicBool = 64;
if(ansicBool != true) printf("This will not print\n");

printf("Any given vlaue other than 0 to ansicBool is evaluated to %i\n", ansicBool);

BOOL objcBOOL = 64;
if(objcBOOL != YES) printf("This might print depnding on your architecture\n");

printf("BOOL will keep whatever value you assign it: %i\n", objcBOOL);

if(!objcBOOL) printf("This will not print\n");

printf("! operator will zero objcBOOL %i\n", !objcBOOL);

if(!!objcBOOL) printf("!! will evaluate objcBOOL value to %i\n", !!objcBOOL);

К вашему удивлению if(objcBOOL != YES)компилятор оценивает его как 1, так YESкак на самом деле это код символа 1, а в глазах компилятора код символа 64, конечно, не равен коду символа 1, поэтому оператор if будет иметь значение, YES/true/1а следующая строка будет запустить. Однако, поскольку boolтип с нулевым нулем всегда оценивается как целочисленное значение 1, вышеуказанная проблема не повлияет на ваш код. Ниже приведены несколько полезных советов, если вы хотите использовать Objective-C BOOLтип против ANSI C boolтипа:

  • Всегда назначайте YESилиNO значение и ничего больше.
  • Конвертировать BOOLтипы с помощью двойного не!! оператор чтобы избежать неожиданных результатов.
  • При проверке YESиспользования if(!myBool) instead of if(myBool != YES)гораздо проще использовать !оператор not и дает ожидаемый результат.
ilgaar
источник
1

Также следует учитывать различия в приведении типов, особенно при работе с битовыми масками, из-за преобразования в подписанные символы:

bool a = 0x0100;
a == true;  // expression true

BOOL b = 0x0100;
b == false; // expression true on !((TARGET_OS_IPHONE && __LP64__) || TARGET_OS_WATCH), e.g. MacOS
b == true;  // expression true on (TARGET_OS_IPHONE && __LP64__) || TARGET_OS_WATCH

Если BOOL является знаковым символом вместо bool, приведение 0x0100 к BOOL просто сбрасывает установленный бит, и полученное значение равно 0.

michaf
источник