(U)Int8 / 16/32/64types的复制Swifts自动数值桥接到Foundation(NSNumber)是否可能?
题
- 是否有可能复制Swifts数值桥接到Foundation:s
NSNumber
引用types,例如Int32
,UInt32
,Int64
和UInt64
types? 具体来说,复制下面介绍的自动分配桥接。
这种解决scheme的示例用法如下:
let foo : Int64 = 42 let bar : NSNumber = foo /* Currently, as expected, error: cannot convert value of type 'Int64' to specified type 'NSNumber */
背景
某些Swift原生数字(值)types可以自动桥接到NSNumber
(引用)types:
Swift数字结构types的实例(如
Int
,UInt
,Float
,Double
和Bool
)不能由AnyObject
types表示,因为AnyObject
仅表示类types的实例。 但是,启用桥接时,可以将Swift数值分配给AnyObject
types的常量和variables作为NSNumber
类的桥接实例 。…
Swift自动将某些本地数字types(如
Int
和Float
桥接到NSNumber
。 这种桥接可以让你从这些types之一创build一个NSNumber
:let n = 42 let m: NSNumber = n
它也允许你传递一个
Int
types的值,例如, 给一个期望NSNumber
的参数 。 …所有以下types都自动桥接到NSNumber:
- Int - UInt - Float - Double - Bool
从互操作性 – 使用cocoa数据types – 数字 。
那么为什么试图复制这个IntXX
/ UIntXX
types?
主要是:好奇心,最近看到一些问题引发了一些问题,其中包含了一些基本问题,包括为什么一个Int
值types看起来可以用AnyObject
(引用)variables来表示,而Int64
则不能; 这自然是由上面所述的桥接解释的。 挑选几个:
- 为什么Swift数组是与AnyObject兼容的?
- 不能下标“[UInt32]”types的值
- 在swift中使用generics数组
上面提到的问题都没有提到,从非桥接typesInt64
, UInt16
等实际实现这种自动桥接到AnyObject
( NSNumber
)的可能性。 这些线程中的答案AnyObject
(正确地)解释为什么AnyObject
不能保存值types,以及IntXX
/ UIntXX
types如何不自动转换为前者的基础types。
其次,对于在32位和64位体系结构上运行的应用程序,有一些狭义的使用情况 – 使用Swift本地数字types隐式转换为AnyObject
,在某些情况下使用例如Int32
或Int64
types将优于Int
。 一个(有些)这样的例子:
- 为什么这个随机函数代码在iPhone 5和5S上崩溃。
是的(有可能):遵守协议_ObjectiveCBridgeable
(以下答案基于使用Swift 2.2和XCode 7.3。)
就像我在思考是否发布或简单地跳过这个问题一样,我偶然发现Swift源代码中的swift/stdlib/public/core/BridgeObjectiveC.swift
,特别是协议_ObjectiveCBridgeable
。 我之前简单地注意到了Swiftdoc.org上的协议,但在后者的当前(空白)蓝图中,我从来没有想过要这么做。 然而,使用Swift源代码中的_ObjectiveCBridgeable
蓝图,我们可以快速地让一些自定义types的本机符合它。
在继续之前,请注意, _ObjectiveCBridgeable
是一个内部/隐藏协议( _UnderScorePreFixedProtocol
),所以基于它的解决scheme可能会在即将到来的Swift版本中突然中断。
启用Int64
桥接到基类NSNumber
作为一个例子,扩展Int64
以符合_ObjectiveCBridgeable
,并随后testing这个相当简单的修复是否足以支持从Int64
到基类NSNumber
的隐式types转换(桥接)。
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 } }
testing:
/* 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
确实足以启用自动的_ObjectiveCBridgeable
-assignment桥接到相应的Foundation类; 在这种情况下, NSNumber
(在Swift中, __NSCFNumber
)。
启用Int8
, UInt8
, Int16
, UInt16
, Int32
, UInt32
,( Int64
)和UInt64
桥接到NSNumber
Int64
与_ObjectiveCBridgeable
的上述一致性可以很容易地修改,以覆盖任何Swift本地整数types,使用下面的NSNumber
转换表。
/* 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 */