>是否可以将Swifts数值桥接复制到Foundation:s NSNumber引用类型,例如: Int32,UInt32,Int64和UInt64类型?具体来说,复制下面介绍的自动按配置桥接.
let foo : Int64 = 42 let bar : NSNumber = foo /* Currently,as expected,error: cannot convert value of type 'Int64' to specified type 'NSNumber */
背景
一些原生的Swift数(值)类型可以自动桥接到NSNumber(引用)类型:
Instances of the Swift numeric structure types,such as
Int
,UInt
,
Float
,Double
,andBool
,cannot be represented by the
AnyObject
type,becauseAnyObject
only represents instances of a
class type. However,when bridging toFoundation
is enabled,Swift
numeric values can be assigned to constants and variables of
AnyObject
type as bridged instances of theNSNumber
class.…
Swift automatically bridges certain native number types,such as
Int
andFloat
,toNSNumber
. This bridging lets you create an
NSNumber
from one of these types:06001
It also allows you to pass a value of type
Int
,for example,to an
argument expecting anNSNumber
. …All of the following types are automatically bridged to NSNumber:
06002
从Interoperability – Working with Cocoa Data Types – Numbers起.
那么为什么要尝试为IntXX / UIntXX类型复制它呢?
主要是:通过最近看到一些问题引发的好奇心,这些问题包括为什么Int值类型看起来可以用AnyObject(引用)变量表示的混淆,而不是Int64,不能;这可以通过上面描述的桥接自然解释.选几个:
> Why is a Swift Array<Int> compatible with AnyObject?
> Cannot subscript a value of type ‘[UInt32]’
> Using generic arrays in swift
然而,上面提到的Q& A:中没有提到从非桥接类型Int64,UInt16等实际实现对AnyObject(NSNumber)的这种自动桥接的可能性.这些线程中的答案非常集中(正确地)解释为什么AnyObject不能保存值类型,以及IntXX / UIntXX类型如何不被桥接以自动转换为前者的基础Foundation类型.
其次:对于在32位和64位体系结构上运行的应用程序,有一些狭窄的用例 – 使用Swift本机数字类型隐式转换为AnyObject,在某些上下文中使用例如Int32或Int64类型优先于Int.一个(有点)这样的例子:
> Why does this random function code crash on an iPhone 5 and 5S.
(以下答案基于使用Swift 2.2和XCode 7.3.)
正如我在思考是否发布或只是跳过这个问题一样,我在Swift源代码中偶然发现了swift/stdlib/public/core/BridgeObjectiveC.swift
,特别是协议_ObjectiveCBridgeable.我之前在Swiftdoc.org时曾经简要地注意过该协议,但是在后者的当前(空)蓝图中,我从来没有考虑过它.使用来自Swift源的_ObjectiveCBridgeable的蓝图,我们可以迅速让一些自定义类型的本地符合它.
在继续之前,请注意_ObjectiveCBridgeable是一个内部/隐藏协议(_UnderscorePreFixedProtocol),因此在即将推出的Swift版本中,基于它的解决方案可能会在没有警告的情况下中断.
启用Int64桥接到Foundation类NSNumber
作为示例,扩展Int64以符合_ObjectiveCBridgeable,并随后测试这个非常简单的修复是否足以从Int64到Foundation类NSNumber保持的隐式类型转换(桥接).
import Foundation extension Int64: _ObjectiveCBridgeable { public typealias _ObjectiveCType = NSNumber public static func _isBridgedToObjectiveC() -> Bool { return true } public static func _getObjectiveCType() -> Any.Type { return _ObjectiveCType.self } public func _bridgeToObjectiveC() -> _ObjectiveCType { return NSNumber(longLong: self) } public static func _forceBridgeFromObjectiveC(source: _ObjectiveCType,inout result: Int64?) { result = source.longLongValue } public static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType,inout result: Int64?) -> Bool { self._forceBridgeFromObjectiveC(source,result: &result) return true } }
测试:
/* Test case: scalar */ let fooInt: Int = 42 let fooInt64: Int64 = 42 var fooAnyObj : AnyObject fooAnyObj = fooInt // OK,natively fooAnyObj = fooInt64 // OK! _ObjectiveCBridgeable conformance successful /* Test case: array */ let fooIntArr: [Int] = [42,23] let fooInt64Arr: [Int64] = [42,23] var fooAnyObjArr : [AnyObject] fooAnyObjArr = fooIntArr // OK,natively fooAnyObjArr = fooInt64Arr // OK! _ObjectiveCBridgeable conformance successful
因此,与_ObjectiveCBridgeable的一致性确实足以启用自动的分配桥接到相应的Foundation类;在这种情况下,NSNumber(在Swift中,__ NSCFNumber).
启用Int8,UInt8,Int16,UInt16,Int32,(Int64)和UInt64桥接到NSNumber
使用下面的NSNumber转换表,可以很容易地修改Int64到_ObjectiveCBridgeable的上述一致性,以涵盖任何Swift原生整数类型.
/* NSNumber initializer: NSNumber native Swift type property -------------------------------- ----------------------------------- init(char: <Int8>) .charValue init(unsignedChar: <UInt8>) .unsignedCharValue init(short: <Int16>) .shortValue init(unsignedShort: <UInt16>) .unsignedShortValue init(int: <Int32>) .intValue init(unsignedInt: <UInt32>) .unsignedIntValue init(longLong: <Int64>) .longLongValue init(unsignedLongLong: <UInt64>) .unsignedLongLongValue */