About Implicit ObjC Bridging & CVarArg

I'm trying to better understand Swift - ObjC bridging and in particular its implicit behaviours & its peculiarities regarding C interop (such as CVarArg). All of these things appear to be poorly documented (but please correct me with references!).

  1. How can I detect whether a value is a native Swift value or a native Objective-C object, without incurring any implicit bridging (eg. tell the difference between an Any that might be an Int or a Double vs. an Any that might be an NSNumber)?
  2. What is the cost associated with "lightweight" operators such as is when they perform bridging? For instance, I was surprised to learn that 5 is NSNumber yields true; does this cause an NSNumber object to get instantiated?
  3. I assume CVarArg is adjacent to this subject and the representation of Swift values as native C values, what can I learn about how Swift treats CVarArg values?
  4. Is there a way to determine whether a value object can be passed to String(format:)/CFStringCreateWithFormatAndArguments as a native value to %f/%s/%p or needs to be passed as an %@ object, ideally without incurring any bridging? I note that 0.5 as CVarArg formats using %f, but 0.5 as NSNumber does not, and neither does 0.5 as NSNumber as CVarArg.

is in that example is most likely creating and then throwing away an NSNumber, yeah. Though in many cases that won’t involve any memory allocation due to tagged pointers.

(Edit: also for *LiteralConvertible things, the compiler is free to skip bridging and just directly construct the destination type. eg let x = “hello” as NSString will create an NSString directly rather than bridging like you’d expect it to)

This is not an entirely satisfactory answer, but without as (edit: or is or as? or as!) a value never converts between class and non-class representations. So for CVarArg, you can rely on the static type when you call the function to know how it’ll be passed.

If you really want to do #1, you should use type(of: value) and ask questions about the current dynamic type rather than the value itself. Types never bridge between class and non-class.

(Edit 2: Why does is behave the way it does? Because it’s simplest if is and as?/as! have exactly the same rules; otherwise they don’t compose.)

Thanks folks; I wonder if there is some generic way to test whether a type is compatible with eg. %f, or would I need to test the value’s type manually against each possible supported type?