6

I was surprised to find that this condition is always true:

let foo: Any = 4
if let object = foo as? AnyObject {
    print("It's an object.")
    //do something with `object` that requires reference semantics
} else {
    print("It's not an object.")
}

It seems that no matter what type foo was originally, it is converted to an instance of a corresponding class. Is there a reliable way to determine whether or not foo is an object?

andyvn22
  • 14,696
  • 1
  • 52
  • 74
  • 3
    Is this in Swift 3? If so, see [AnyObject not working in Xcode8 beta6?](http://stackoverflow.com/questions/39033194/anyobject-not-working-in-xcode8-beta6) – Hamish Aug 27 '16 at 19:27
  • 1
    The linked thread is not answering the question "how we can distinguish the content of `Any` if it's a class instance or something else?". Why this is marked as "duplicate"? – OOPer Aug 27 '16 at 19:39
  • @MartinR, the OP's description is "Is there a reliable way to determine whether or not foo is an object?". Not insisting to use `is` or `as?`, so there may be another way. – OOPer Aug 27 '16 at 20:05
  • Thanks for the link @Hamish; it's great to know that I'm not the only one who was surprised by this behavior. – andyvn22 Aug 27 '16 at 20:19

2 Answers2

4

UPDATE

The code I have shown below is reported as not working in release build. (Please see Paul Cantrell's comment below.)

Apologies for my "as far as I tested" was too limited.

I'll update this answer when I find some further info about this.


I'm not sure we can see this behaviour in the next beta (or GM or Released version...), but this works as you expect in Xcode 8 beta 6.

let foo: Any = 4
if type(of: foo) is AnyClass {
    print("It's an object.")
    let object = foo as AnyObject
    //do something with `object` that requires reference semantics
} else {
    print("It's not an object.") //->It's not an object.
}

class MyClass {}
let bar: Any = MyClass()
if type(of: bar) is AnyClass {
    print("It's an object.") //->It's an object.
    let object = foo as AnyObject
    //do something with `object` that requires reference semantics
} else {
    print("It's not an object.")
}

let baz: Any = Array<AnyObject>()
if type(of: baz) is AnyClass {
    print("It's an object.")
    let object = foo as AnyObject
    //do something with `object` that requires reference semantics
} else {
    print("It's not an object.") //->It's not an object.
}

I cannot check all possible cases, so there may be some edge cases where this does not work. But as far as I tested, this seems to work as expected.

OOPer
  • 47,149
  • 6
  • 107
  • 142
1

Perhaps you want to look at Mirror which allows a degree of introspection. Documentation is here

import Foundation

func prettyPrint(_ any: Any) -> String {
    let m = Mirror(reflecting: any)
    switch m.displayStyle {
    case .some(.class): // ****
        return "Class"
    case .some(.collection):
        return "Collection, \(m.children.count) elements"
    case .some(.tuple):
        return "Tuple, \(m.children.count) elements"
    case .some(.dictionary):
        return "Dictionary, \(m.children.count) elements"
    case .some(.set):
        return "Set, \(m.children.count) elements"
    case .some(.optional):
        return "Optional"
    case .some(.enum):
        return "Enum"
    case .some(.struct):
        return "Struct"
    default:
        return "\(String(describing: m.displayStyle))"
    }
}
class A {}

prettyPrint([1, 2, 3]) // "Collection, 3 elements"
prettyPrint(Set<String>()) // "Set, 0 elements"
prettyPrint([1: 2, 3: 4]) // "Dictionary, 2 elements"
prettyPrint((1, 2, 3)) // "Tuple, 3 elements"
prettyPrint(3) // "nil"
prettyPrint("3") // "nil"
prettyPrint(NSObject()) // "Class"
prettyPrint(NSArray(array:[1, 2, 3])) // "Collection, 3 elements"
prettyPrint(A()) // "Class"

// prettyPrint(nil) // Compile time error "Nil is not compatible with Any
Grimxn
  • 22,115
  • 10
  • 72
  • 85