Is there a correct way to determine that an NSNumber is derived from a Bool using Swift?

扶醉桌前 提交于 2019-11-27 14:45:37

You can ask the same question for Objective-C, and here is an answer in Objective-C - which you can call from, or translate into, Swift.

NSNumber is toll-free bridged to CFNumberRef, which is another way of saying an NSNumber object is in fact a CFNumber one (and vice-versa). Now CFNumberRef has a specific type for booleans, CFBooleanRef, and this is used when creating a boolean CFNumberRef aka NSNumber *... So all you need to do is check whether your NSNumber * is an instance of 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;
}

Note: You may notice that NSNumber/CFNumber objects created from booleans are actually pre-defined constant objects; one for YES, one for NO. You may be tempted to rely on this for identification. However, though is currently appears to be true, and is shown in Apple's source code, to our knowledge it is not documented so should not be relied upon.

HTH

Addendum

Swift code translation (by 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
}

The first one is the correct one.

NSNumber is an Objective-C class. It is built for Objective-C. It stores the type using the type encodings of Objective-C. So in Objctive-C the best solution would be:

number.objCType[0] == @encoding(BOOL)[0] // or string compare, what is not necessary here

This ensures that a change of the type encoding will work after re-compile.

AFAIK you do not have @encoding() in Swift. So you have to use a literal. However, this will not break, because @encoding() is replaced at compile time and changing the encodings would break with compiled code. Unlikely.

The second approach uses a internal identifier. This is likely subject of change.

I think the third approach will have false positives.

Don't rely on the class name as it likely belongs to a class cluster, and it is an implementation detail (and therefore subject to change).

Unfortunately, the Objective-C BOOL type was originally a just typedef for a signed char in C, which is always encoded as c (this is the 99 value you are seeing, since c in ASCII is 99).

In modern Objective-C, I believe the BOOL type is an actual Boolean type (i.e. no longer just a typedef for signed char) but for compatibility, it still encodes as c when given to @encode().

So, there's no way to tell whether the 99 originally referred to a signed char or a BOOL, as far as NSNumber is concerned they are the same.

Maybe if you explain why you need to know whether the NSNumber was originally a BOOL, there may be a better solution.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!