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?
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 anAnyObject
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
(akaAnyObject.Type
).For generics, if you want to check whether the static type of
T
is a reference type, you can do:If you want to check whether the dynamic type of a value typed as
T
is a reference type (such asval
in your example), you can use thetype(of:)
function on the unwrapped value, as the aforementioned Q&A suggests:The difference between these two approaches is that when
T
is of typeAny
(or a nonAnyObject
abstract type),T.self is AnyClass
will returnfalse
(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 whetherval
itself is a reference type.