In Swift, how is it that AnyObject
supports subscripts, even for types that are't subscriptable? Example:
let numbers: AnyObject = [11, 22, 33]
numbers[0] // returns 11
let prices: AnyObject = ["Bread": 3.49, "Pencil": 0.5]
prices["Bread"] // returns 3.49
let number: AnyObject = 5
number[0] // return nil
let number: AnyObject = Int(5)
number[0] // return nil
Yet if my number
is declared as Int
then it's a syntax error:
let number: Int = 5
number[0] // won't compile
Interestingly, Any
doesn't have subscript support.
This works only if you import Foundation
, as Swift is doing in that case some bridging to Objective-C
types - a NSArray
-like object in this case.
import Foundation
let numbers: AnyObject = [11, 22, 33] as AnyObject
type(of: numbers) //_SwiftDeferredNSArray.Type
If you don't import Foundation
, then you are not even allowed to make the assignment (because a Swift array is a struct and not an object).
let numbers: AnyObject = [11, 22, 33] // error: contextual type 'AnyObject' cannot be used with array literal
You can cast to Any
, though:
let numbers: Any = [11, 22, 33]
type(of: numbers) // Array<Int>.Type
Why does the import Foundation
does the trick? This is documented in the AnyObject
type description:
/// When used as a concrete type, all known `@objc` methods and
/// properties are available, as implicitly-unwrapped-optional methods
/// and properties respectively, on each instance of `AnyObject`. For
/// example:
///
/// class C {
/// @objc func getCValue() -> Int { return 42 }
/// }
///
/// // If x has a method @objc getValue()->Int, call it and
/// // return the result. Otherwise, return nil.
This means you can even call methods on your array that don't necessarily exist on NSArray
, but exists in the Objective-C
world, like for example:
numbers.lowercaseString // nil
and Swift will gracefully return you a nil
value instead of throwing you a nasty object does not recognises selector
exception, like it would happen in Objective-C. If this is good or bad, remains to debate :)
Update The above seems to work only for properties, and property-like methods, if you try to use an Objective-C method, then you'll run into the unrecognized selector issue:
import Foundation
@objc class TestClass: NSObject {
@objc var someProperty: Int = 20
@objc func someMethod() {}
}
let numbers: AnyObject = [11, 22, 33] as AnyObject
numbers.lowercaseString // nil
numbers.someMethod // nil
numbers.someMethod() // unrecognized selector
numbers.stringByAppendingString("abc") // unrecognized selector
It has to do with the bridging of types when assigning a value to an object of type AnyObject
:
let numbers: AnyObject = [11, 22, 33]
print(numbers.dynamicType) // _SwiftDeferredNSArray.Type
let prices: AnyObject = ["Bread": 3.49, "Pencil": 0.5]
print(prices.dynamicType) // _NativeDictionaryStorageOwner<String, Double>.Type
let number: AnyObject = 5
print(number.dynamicType) // __NSCFNumber.Type
let anotherNumber: Int = 5
print(anotherNumber.dynamicType) // Int.Type
Behind the scenes, _SwiftDeferredNSArray.Type
, _NativeDictionaryStorageOwner<String, Double>.Type
, and __NSCFNumber.Type
must support subscripts while Int.Type
does not.
This is assuming you have imported Foundation. For an explanation with pure Swift types, see Cristik's answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With