Существует ли правильный способ определить, что 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 8

3 ответа:

Вы можете задать тот же вопрос для Objective-C, и вот ответ в Objective-C, который вы можете вызвать или перевести в Swift.

NSNumber является бесплатным мостом к CFNumberRef, что является еще одним способом сказать, что объект NSNumber на самом деле является объектом CFNumber (и наоборот). Теперь CFNumberRef имеет определенный тип для булевых чисел, CFBooleanRef, и это используется при создании булевого CFNumberRef aka NSNumber *... Поэтому все, что вам нужно сделать, это проверить, является ли ваш 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-C BOOL изначально был просто typedef для signed char в C, который всегда кодируется как c (это значение 99, которое вы видите, так как c в ASCII равно 99).

В современном Objective-C я считаю, что тип BOOL является фактическим Булевым типом (т. е. больше не просто typedef для signed char), а для совместимость, он все еще кодирует как c, Когда дано @encode().

Таким образом, нет никакого способа сказать, относится ли 99 первоначально к signed char или к BOOL, поскольку NSNumber относится к одному и тому же.

Возможно, если вы объясните, почему вам нужно знать, был ли NSNumber изначально BOOL, может быть, есть лучшее решение.