3

In Swift 3 I am no longer able to check whether generic variable type is class (AnyObject) or not. Following code returns true for isObject even though specific type T and passed value is struct and not class. In Swift 2.3 and 2.2 it works as expected and isObject is false.

struct Foo<T>
{
    var value: Any?
    var isObject: Bool = false

    init (val: T?)
    {
        if val != nil
        {
            // following line shows warnings in Swift 3
            // conditional cast from 'T?' to 'AnyObject' always succeeds
            // 'is' cast is always true
            isObject = val is AnyObject

            self.value = val
        }
    }
}

struct Bar
{
    var bar = 0
}

let b = Foo<Bar>(val: Bar())

print(b.isObject) // -> true

How can I make it work properly in Swift 3?

Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
  • Even `90 is AnyObject` returns `true`. Sounds like a bug to me. – MirekE Sep 17 '16 at 12:19
  • 1
    Related: [AnyObject not working in Xcode8 beta6?](http://stackoverflow.com/questions/39033194/anyobject-not-working-in-xcode8-beta6) – Hamish Sep 17 '16 at 12:21

1 Answers1

9

In Swift 3, everything is bridgeable to AnyObject due to the introduction of _SwiftValue (see this Q&A for more info), that can wrap anything that isn't directly bridgeable to Objective-C in an opaque Objective-C compatible box.

Therefore is AnyObject will always be true, as anything can be represented as an AnyObject via wrapping in a _SwiftValue.

One way to check whether a value is a reference type (as shown in this Q&A) is to type-check the type of the value against the metatype of AnyObject, AnyClass (aka AnyObject.Type).

For generics, if you want to check whether the static type of T is a reference type, you can do:

isObject = T.self is AnyClass

If you want to check whether the dynamic type of a value typed as T is a reference type (such as val in your example), you can use the type(of:) function on the unwrapped value, as the aforementioned Q&A suggests:

if let val = val {
    isObject = type(of: val) is AnyClass

    // ...
}

The difference between these two approaches is that when T is of type Any (or a non AnyObject abstract type), T.self is AnyClass will return false (which could be useful if you want a box where the value could be a reference or value type) – type(of: val) is AnyClass however, will return whether val itself is a reference type.

Community
  • 1
  • 1
Hamish
  • 78,605
  • 19
  • 187
  • 280