You use Swift AnyObject
wrong.
So you probably read The Swift Programming Language book, where this piece can be found:
Swift provides two special types for working with nonspecific types:
Any
can represent an instance of any type at all, including function types.AnyObject
can represent an instance of any class type.Use
Any
andAnyObject
only when you explicitly need the behavior and capabilities they provide. It is always better to specify the types you expect to work with in your code.
However, this paragraph isn't accurate. Some may say it's misleading. A little more accurate is a documentation:
You use
AnyObject
when you need the flexibility of an untyped object or when you use bridged Objective-C methods and properties that return an untyped result.AnyObject
can be used as the concrete type for an instance of any class, class type, or class-only protocol.
I emphasized the part that is actually correct and makes the AnyObject
distinct from Any
.
Maybe you read about Class Only Protocols In Swift 4 and learned, that it's preferable now to use AnyObject
rather than class
keyword:
Since Swift 4 the preferred way to declare a class-only protocol is to use
AnyObject
rather thanclass
It's unfortunate because AnyObject
is more than just a class
. Actually, AnyObject may be a class type and the value type as well.
Array<AnyObject>
let array:[AnyObject] = []
if array is [Int] {
// yes [AnyObject] is [Int]
}
if array is [String] {
// yes [AnyObject] is [String]
}
not what I expected. Array<Element>
and Int
is a value type, struct:
public struct Int : FixedWidthInteger, SignedInteger { }
public struct Array<Element> { }
definitely not a class 😳 But it's an array so who knows.
Value AnyObject
Now. The book taught me that AnyObject
could represent an instance of any class type. So... what if I build a generic class where the value type supposes to be an object rather than value type. I should use AnyObject
, so I defined Container
class:
class Container<T> where T: AnyObject {
init(value: T) {
print("Initialize with object \(value)")
}
}
my container has a value, for this exercise let's define a Dog as one possible value and instantiate the dog named "Rex":
class Dog {
let name: String
init(name: String) {
self.name = name
}
}
let rex = Dog(name: "Rex")
Cats are different. Let's define a Cat as a struct. This cat is named "Bella":
struct Cat {
let name: String
}
let bella = Cat(name: "Bella")
Quiz
You're correct! The rex
variable is an object (class Dog
), so it is an AnyObject
. Easy to check:
if rex is AnyObject { // warning: 'is' test is always true
print("\(rex) is AnyObject")
}
You're correct! The bella
variable is a value type (struct Cat
), so it is an AnyObject
. Easy to check:
if bella is AnyObject {
print("\(bella) is AnyObject")
}
Wait WAT?
As pointed earlier, bella
variable is a struct hence it's a value type. AnyObject
that can represent an instance of any class type. Nobody said it could represent the value type as well.
Isn't that why we have Any
type to represent both already?
Some may say: it will crash at runtime. However, it won't.
let container = Container(value: bella as AnyObject)
container
instance is of type Container<Swift.AnyObject>
. So what? I just put a value where an object is expected. Why? Because I can.
What can possibly go wrong?
Possible consequences of use AnyObject
is API misuse: pass the value where Object is expected.
Rules
The rules for proper use of AnyObject
right would be:
- Don't use
AnyObject
as a generics constraint. - Don't use
is AnyObject
noras? AnyObject
to check if a variable is a class type. - Don't use
AnyObject
. - Use
class
for a class-only protocol. - Use
Any
.