(U)Int8 / 16/32/64types的复制Swifts自动数值桥接到Foundation(NSNumber)是否可能?

  • 是否有可能复制Swifts数值桥接到Foundation:s NSNumber引用types,例如Int32UInt32Int64UInt64types? 具体来说,复制下面介绍的自动分配桥接。

这种解决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的实例(如IntUIntFloatDoubleBool )不能由AnyObjecttypes表示,因为AnyObject仅表示类types的实例。 但是,启用桥接时,可以将Swift数值分配给AnyObjecttypes的常量和variables作为NSNumber类的桥接实例

Swift自动将某些本地数字types(如IntFloat桥接到NSNumber 。 这种桥接可以让你从这些types之一创build一个NSNumber

 let n = 42 let m: NSNumber = n 

它也允许你传递一个Inttypes的值,例如, 给一个期望NSNumber的参数 。 …

所有以下types都自动桥接到NSNumber:

 - Int - UInt - Float - Double - Bool 

从互操作性 – 使用cocoa数据types – 数字 。

那么为什么试图复制这个IntXX / UIntXXtypes?

主要是:好奇心,最近看到一些问题引发了一些问题,其中包含了一些基本问题,包括为什么一个Int值types看起来可以用AnyObject (引用)variables来表示,而Int64则不能; 这自然是由上面所述的桥接解释的。 挑选几个:

  • 为什么Swift数组是与AnyObject兼容的?
  • 不能下标“[UInt32]”types的值
  • 在swift中使用generics数组

上面提到的问题都没有提到,从非桥接typesInt64UInt16等实际实现这种自动桥接到AnyObjectNSNumber )的可能性。 这些线程中的答案AnyObject (正确地)解释为什么AnyObject不能保存值types,以及IntXX / UIntXXtypes如何不自动转换为前者的基础types。

其次,对于在32位和64位体系结构上运行的应用程序,有一些狭义的使用情况 – 使用Swift本地数字types隐式转换为AnyObject ,在某些情况下使用例如Int32Int64types将优于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 )。


启用Int8UInt8Int16UInt16Int32UInt32 ,( 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 */