Если BOOL имеет хорошее краткое название, достаточно легко записать:
myBOOL = !myBOOL;
Но что, если BOOL имеет длинное имя?
objectWithLongishName.memberWithLongishName.submember.myBOOL = !(objectWithLongishName.memberWithLongishName.submember.myBOOL);
... не выглядит настолько симпатичным.
Я задаюсь вопросом, существует ли простой способ переключить BOOL, не вводя его имя дважды?
Вот еще один:
MyBooleanYaddaYadda ^= YES;
Это довольно хрупко - он сломается в устаревшем коде C, который подразумевает, что любое ненулевое целое число оценивается как истинное. Но опять же, как и код фреймворка Apple - я встречал случаи в Какао, когда ненулевое, отличное от единицы int, передаваемое как BOOL, не приводило бы к такому же эффекту, как передача YES.
Однако он не полагается на битовую комбинацию YES - только на то, что NO равно 0. Что в значительной степени само собой разумеющееся, учитывая то, как C интерпретирует целые числа как логические значения. Кроме того, он не предполагает фактического типа данных BOOL (который в Какао, кстати, подписанный char
).
Битовый шаблон YES в Какао равен 1. Но это не универсальное соглашение. На некоторых платформах без встроенного логического типа данных целочисленная константа, которая служит логическим значением ИСТИНА, равна -1
- все единичные биты. Это 0xFFFFFFFF, если интерпретировать как беззнаковый. Такое кодирование имеет нечеткое преимущество, заключающееся в том, что побитовое НЕ (оператор ~ в C) эквивалентно логическому НЕ (оператор! В C). То есть ~ 0xFFFFFFFF равно 0, т.е. е. ~ ИСТИНА - ЛОЖЬ. Не работает, если ИСТИНА определено как 1.
В (Objective-)C нет очевидного способа сделать то, что вы описали (без использования макроса препроцессора), но смотрите ответ Севы для возможного (хотя потенциально хрупкого) решения. Что более важно, что-то вроде objectWithLongishName.memberWithLongishName.submember.myBOOL
указывает на нарушение закона Деметры; вы должны предоставлять submember
непосредственно любой единице кода, которой нужен доступ к submember.myBOOL
.
Написать метод для класса submember
, который переключает его для вас?
- (void) toggleMyBOOL {
self.myBool = !self.myBool;
}
Тогда вы можете сделать:
[objectWithLongishName.memberWithLongishName.submember toggleMyBOOL];
Использовать XOR. В C это ^.
BOOL x = YES;
x ^= YES; // it's now NO
x ^= YES; // it's now YES
x ^= YES; // it's now NO
x ^= YES; // it's now YES
x ^= YES; // it's now NO
x ^= YES; // it's now YES
...
Edit: очевидно, кто-то уже опубликовал это. Думаю, я должен сказать, что на самом деле никогда не использовал это в коде. : -)
У вас прекрасный набор ответов, сосредоточенных на переключении ДА на НЕТ или наоборот, но нет ответов, которые касались бы того, что представляется архитектурной проблемой в коде.
Ну, некоторые ответы. Я ослеп.
А именно, у вас есть вот это:
objectWithLongishName.memberWithLongishName.submember.myBOOL =
!(objectWithLongishName.memberWithLongishName.submember.myBOOL);
Это пахнет потенциальным нарушением инкапсуляции. В частности (и если предположить, что это слой модели), это означает, что связность подграфа объектов открыто выставляется напоказ - сплющивается в, фактически - точку входа этого пути; чем бы ни был objectWithLongishName
, он теперь должен обладать довольно глубокими знаниями о внутренностях объектов на остальной части пути.
Обычно вы не проникаете глубоко в слой модели вдоль ключевых путей, чтобы редактировать состояние за пределами слоя Cocoa Bindings (и даже он немного хрупок).
Иногда такие длинные пути действительно имеют смысл. В таком случае, я бы оставил убер-вербозную форму, которую вы привели выше, как визуальное указание на то, что инкапсуляция целенаправленно разрушается.
#define NOT(b) (b) = !(b)
NOT(MyBooleanVariableWithAFreakishlyLongName);
Или, если это Objective C++:
inline void NOT(BOOL &b)
{
b = !b;
}