Swift provides serveral special types for working with nonspecific types:
AnyObject
represents an instance of any class type.AnyClass
represents a class type rather than an instance of that class.Any
represents an instance of any type at all, including function types.any
represents existential types, can only be applied to protocols and protocol compositions, or metatypes thereof.and some tools to work with nonspecific types:
is
operatoras
, as?
, as!
type(of:)
Nonspecific types can be useful in certain scenarios because they provide a lot of flexibility in working with different types of data. Here are some potential benefits and use cases:
id
.Because nonspecific types can represent any type of data, it’s possible to accidentally pass the wrong type of data to a function or method. This can lead to runtime errors or unexpected behavior.
It’s a good idea to use more specific types whenever possible, as they can provide increased type safety and make your code easier to reason about.
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.
The flexible behavior of the AnyObject
protocol is similar to Objective-C’s id
type. For this reason, imported Objective-C types frequently use AnyObject
as the type for properties, method parameters, and return values.
class MyClass {
var myProperty: String
init(myProperty: String) {
self.myProperty = myProperty
}
}
class MyOtherClass {
var myOtherProperty: Int
init(myOtherProperty: Int) {
self.myOtherProperty = myOtherProperty
}
}
let instanceOfMyClass = MyClass(myProperty: "Hello, world!")
let instanceOfMyOtherClass = MyOtherClass(myOtherProperty: 42)
let array: [AnyObject] = [instanceOfMyClass, instanceOfMyOtherClass]
for object in array {
if let myClassInstance = object as? MyClass {
print("Instance of MyClass with property: \(myClassInstance.myProperty)")
} else if let myOtherClassInstance = object as? MyOtherClass {
print("Instance of MyOtherClass with property: \(myOtherClassInstance.myOtherProperty)")
}
}
// Prints:
// Instance of MyClass with property: Hello, world!
// Instance of MyOtherClass with property: 42
Because we’re using AnyObject
, we’re able to store objects of different types in the same array. However, we need to use conditional casts to determine the specific type of each object in the array in order to work with its properties.
typealias AnyClass = AnyObject.Type
You can use the AnyClass
protocol as the concrete type for an instance of any class. When you do, all known @objc
class methods and properties are available as implicitly unwrapped optional methods and properties, respectively.
import Foundation
class IntegerRef {
@objc class func getDefaultValue() -> Int {
return 42
}
}
func getDefaultValue(_ c: AnyClass) -> Int? {
return c.getDefaultValue?()
}
print(getDefaultValue(IntegerRef.self))
// Prints "Optional(42)"
print(getDefaultValue(NSString.self))
// Prints "nil"
In Swift, Any
can represent an instance of any type at all, including function types, broader than AnyObject
. This also explains why Any
isn’t a protocol.
Here’s an example of using Any to work with a mix of different types, including function types and nonclass types. The example creates an array called things, which can store values of type Any:
var things: [Any] = []
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append({ (name: String) -> String in "Hello, \(name)" })
any
In Swift, a plain protocol name in type context means an existential type. They have significant limitations and performance implications, and also significantly more expensive than using concrete types. In addition to heap allocation and reference counting, code using existential types incurs pointer indirection and dynamic method dispatch that cannot be optimized away.
Since Swift 5.6, anywhere that an existential type can be used, the any
keyword can be used to explicitly denote an existential type:
// Swift 5 mode
protocol P {}
protocol Q {}
struct S: P, Q {}
let p1: P = S() // 'P' in this context is an existential type
let p2: any P = S() // 'any P' is an explicit existential type
let pq1: P & Q = S() // 'P & Q' in this context is an existential type
let pq2: any P & Q = S() // 'any P & Q' is an explicit existential type
Explicit any
can only be applied to protocols and protocol compositions, or metatypes thereof; any cannot be applied to nominal types, structural types, type parameters, and protocol metatypes:
struct S {}
let s: any S = S() // error: 'any' has no effect on concrete type 'S'
func generic<T>(t: T) {
let x: any T = t // error: 'any' has no effect on type parameter 'T'
}
let f: any ((Int) -> Void) = generic // error: 'any' has no effect on concrete type '(Int) -> Void'
Using any Any
and any AnyObject
are redundant. Any
and AnyObject
are already special types in the language, and their existence isn’t nearly as harmful as existential types for regular protocols because the type-erasing semantics is already explicit in the name.