Swift AnyObject的下标,它来自哪里?

时间:2020-12-05 11:33:57

In Swift, how is it that AnyObject supports subscripts, even for types that are't subscriptable? Example:

在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

Yet if my number is declared as Int then it's a syntax error:

然而,如果我的数字被声明为Int,那么这是一个语法错误:

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

Interestingly, Any doesn't have subscript support.

有趣的是,Any没有下标支持。

2 个解决方案

#1


12  

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.

这只有在你导入Foundation时才有效,因为在这种情况下Swift正在做一些桥接到Objective-C类型 - 在这种情况下类似于NSArray的对象。

import Foundation

let numbers: AnyObject = [11, 22, 33]
numbers.dynamicType //_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).

如果不导入Foundation,则甚至不允许进行赋值(因为Swift数组是结构而不是对象)。

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

You can cast to Any, though:

但是你可以强制转换为Any:

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

Why does the import Foundation does the trick? This is documented in the AnyObject type description:

为什么导入基金会会这样做?这在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.  

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:

这意味着你甚至可以在数组上调用NSArray上不一定存在的方法,但是存在于Objective-C世界中,例如:

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 :)

并且Swift将优雅地返回一个nil值,而不是抛弃一个讨厌的对象,不会识别选择器异常,就像它会在Objective-C中发生一样。如果这是好还是坏,仍然有争议:)

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:

更新以上似乎仅适用于属性和类似属性的方法,如果您尝试使用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


5  

It has to do with the bridging of types when assigning a value to an object of type AnyObject:

在为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.

在幕后,_SwiftDeferredNSArray.Type,_NativeDictionaryStorageOwner .Type和__NSCFNumber.Type必须支持下标,而Int.Type则不支持。 ,double>

This is assuming you have imported Foundation. For an explanation with pure Swift types, see Cristik's answer.

这假设您已导入Foundation。有关纯Swift类型的解释,请参阅Cristik的答案。

#1


12  

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.

这只有在你导入Foundation时才有效,因为在这种情况下Swift正在做一些桥接到Objective-C类型 - 在这种情况下类似于NSArray的对象。

import Foundation

let numbers: AnyObject = [11, 22, 33]
numbers.dynamicType //_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).

如果不导入Foundation,则甚至不允许进行赋值(因为Swift数组是结构而不是对象)。

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

You can cast to Any, though:

但是你可以强制转换为Any:

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

Why does the import Foundation does the trick? This is documented in the AnyObject type description:

为什么导入基金会会这样做?这在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.  

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:

这意味着你甚至可以在数组上调用NSArray上不一定存在的方法,但是存在于Objective-C世界中,例如:

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 :)

并且Swift将优雅地返回一个nil值,而不是抛弃一个讨厌的对象,不会识别选择器异常,就像它会在Objective-C中发生一样。如果这是好还是坏,仍然有争议:)

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:

更新以上似乎仅适用于属性和类似属性的方法,如果您尝试使用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


5  

It has to do with the bridging of types when assigning a value to an object of type AnyObject:

在为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.

在幕后,_SwiftDeferredNSArray.Type,_NativeDictionaryStorageOwner .Type和__NSCFNumber.Type必须支持下标,而Int.Type则不支持。 ,double>

This is assuming you have imported Foundation. For an explanation with pure Swift types, see Cristik's answer.

这假设您已导入Foundation。有关纯Swift类型的解释,请参阅Cristik的答案。