Существует ли правильный способ определить, что NSNumber является производным от Bool с помощью Swift?
NSNumber, содержащий Bool, легко спутать с другими типами, которые могут быть обернуты в класс NSNumber:
NSNumber(bool:true).boolValue // true
NSNumber(integer: 1).boolValue // true
NSNumber(integer: 1) as? Bool // true
NSNumber(bool:true) as? Int // 1
NSNumber(bool:true).isEqualToNumber(1) // true
NSNumber(integer: 1).isEqualToNumber(true) // true
Однако информация о его первоначальном типе сохраняется, как мы видим здесь:
NSNumber(bool:true).objCType.memory == 99 // true
NSNumber(bool:true).dynamicType.className() == "__NSCFBoolean" // true
NSNumber(bool:true).isEqualToValue(true) || NSNumber(bool:true).isEqualToValue(false) //true
Вопрос в том, какой из этих подходов является лучшим (и / или самым безопасным) для определения того, когда Bool был завернут в NSN-число, а не что-то другое? Все ли они одинаково действительны? Или есть другое, лучшее решение?3 ответа:
Вы можете задать тот же вопрос для Objective-C, и вот ответ в Objective-C, который вы можете вызвать или перевести в Swift.
NSNumber
является бесплатным мостом кCFNumberRef
, что является еще одним способом сказать, что объектNSNumber
на самом деле является объектомCFNumber
(и наоборот). ТеперьCFNumberRef
имеет определенный тип для булевых чисел,CFBooleanRef
, и это используется при создании булевогоCFNumberRef
akaNSNumber *
... Поэтому все, что вам нужно сделать, это проверить, является ли вашNSNumber *
экземпляромCFBooleanRef
:- (BOOL) isBoolNumber:(NSNumber *)num { CFTypeID boolID = CFBooleanGetTypeID(); // the type ID of CFBoolean CFTypeID numID = CFGetTypeID((__bridge CFTypeRef)(num)); // the type ID of num return numID == boolID; }
Примечание: Вы можете заметить, что
NSNumber
/CFNumber
объекты, созданные из булевых символов, на самом деле являются предопределенными постоянными объектами; один дляYES
, один дляNO
. У вас может возникнуть искушение положиться на это для идентификации. Однако, хотя в настоящее время это, по-видимому, верно и показано в исходном коде Apple, насколько нам известно, это не документировано, поэтому не следует полагаться.HTH
Добавление
Swift код перевод (по GoodbyeStackOverflow):
func isBoolNumber(num:NSNumber) -> Bool { let boolID = CFBooleanGetTypeID() // the type ID of CFBoolean let numID = CFGetTypeID(num) // the type ID of num return numID == boolID }
Первый из них является правильным.
NSNumber
это класс Objective-C. Он построен для Objective-C. Он хранит тип, используя кодировку типа Objective-C. Поэтому в Objective-C лучшим решением было бы:number.objCType[0] == @encoding(BOOL)[0] // or string compare, what is not necessary here
Это гарантирует, что изменение кодировки типа будет работать после повторной компиляции.
AFAIK у вас нет
@encoding()
в Swift. Поэтому вы должны использовать буквальное выражение. Однако это не приведет к разрыву, так как@encoding()
заменяется во время компиляции и изменение кодировок приведет к тому, что разрыв с компилированным кодом. Вряд ли.Второй подход использует внутренний идентификатор. Это, вероятно, подлежит изменению.
Я думаю, что третий подход будет иметь ложные срабатывания.
Не полагайтесь на имя класса, так как оно, скорее всего, принадлежит кластеру классов и является деталью реализации (и поэтому может быть изменено).
К сожалению, тип Objective-CBOOL
изначально был просто typedef дляsigned char
в C, который всегда кодируется какc
(это значение99
, которое вы видите, так какc
в ASCII равно 99).В современном Objective-C я считаю, что тип
BOOL
является фактическим Булевым типом (т. е. больше не просто typedef дляsigned char
), а для совместимость, он все еще кодирует какc
, Когда дано@encode()
.Таким образом, нет никакого способа сказать, относится ли
Возможно, если вы объясните, почему вам нужно знать, был ли99
первоначально кsigned char
или кBOOL
, посколькуNSNumber
относится к одному и тому же.NSNumber
изначальноBOOL
, может быть, есть лучшее решение.