Индекс 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

Но если мой number объявлен как Int, то это синтаксическая ошибка:

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

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


person Steve Kuo    schedule 21.01.2016    source источник
comment
Релевантно: stackoverflow. ком/вопросы/24154304/   -  person dfrib    schedule 22.01.2016
comment
Вопрос очень интересный, чтобы понять, что за кулисами, но довольно теоретический. На практике вы не стали бы использовать аннотацию типа AnyObject для объекта, чей правильный тип может вывести компилятор, и вы, разработчик, точно знаете. Apple в любом случае настоятельно не рекомендует использовать (такие) аннотации типов.   -  person vadian    schedule 22.01.2016
comment
NSJSONSerialization.JSONObjectWithData возвращает AnyObject   -  person Steve Kuo    schedule 22.01.2016


Ответы (2)


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

import Foundation

let numbers: AnyObject = [11, 22, 33] as AnyObject
type(of: numbers) //_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]    
type(of: numbers) // 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] as AnyObject
numbers.lowercaseString                // nil
numbers.someMethod                     // nil
numbers.someMethod()                   // unrecognized selector
numbers.stringByAppendingString("abc") // unrecognized selector
person Cristik    schedule 21.01.2016
comment
Отличные находки и отличный ответ. Мой ответ предполагает, что Foundation был импортирован. Спасибо за чистое объяснение Swift! - person JAL; 22.01.2016

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

person JAL    schedule 21.01.2016