Swift AnyObject, откуда он взялся?

В Swift, как AnyObject поддерживает индексы, даже для типов, которые не подрываются? Пример:

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

Однако, если my number объявлен как Int, то это синтаксическая ошибка:

let number: Int = 5
number[0]        // won't compile

Интересно, что Any не поддерживает индексы.

Ответы

Ответ 1

Это работает, только если вы импортируете Foundation, так как Swift делает в этом случае некоторую привязку к Objective-C типам - объект NSArray -like в этом случае.

import Foundation

let numbers: AnyObject = [11, 22, 33]
numbers.dynamicType //_SwiftDeferredNSArray.Type

Если вы не импортируете Foundation, вам даже не разрешено выполнять присвоение (поскольку массив Swift является структурой, а не объектом).

let numbers: AnyObject = [11, 22, 33] // error: contextual type 'AnyObject' cannot be used with array literal

Вы можете использовать Any, но:

let numbers: Any = [11, 22, 33]    
numbers.dynamicType // Array<Int>.Type

Почему import Foundation делает трюк? Это описано в описании типа AnyObject:

/// 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.  

Это означает, что вы можете даже вызывать методы в вашем массиве, которые не обязательно существуют на NSArray, но существуют в мире Objective-C, например, например:

numbers.lowercaseString       // nil

и Swift будет изящно возвращать вам значение nil вместо того, чтобы бросать вам неприятное исключение object does not recognises selector, как это было бы в Objective-C. Если это хорошо или плохо, остается обсуждать:)

Обновление Вышеприведенное, похоже, работает только для свойств и методов, подобных свойствам, если вы попытаетесь использовать метод Objective-C, тогда вы столкнетесь с проблемой нераспознанного селектора:

import Foundation

@objc class TestClass: NSObject {
    @objc var someProperty: Int = 20
    @objc func someMethod() {}
}

let numbers: AnyObject = [11, 22, 33]
numbers.lowercaseString                // nil
numbers.someMethod                     // nil
numbers.someMethod()                   // unrecognized selector
numbers.stringByAppendingString("abc") // unrecognized selector

Ответ 2

Он связан с мостом типов при назначении значения объекту типа 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

За кулисами _SwiftDeferredNSArray.Type, _NativeDictionaryStorageOwner<String, Double>.Type и __NSCFNumber.Type должны поддерживать индексы, а Int.Type - нет.

Предполагается, что вы импортировали Foundation. Для объяснения с помощью чистых типов Swift см. ответ Кристика.