问题描述
问题
- 是否可以复制桥接到Foundation:s
NSNumber
引用类型的Swifts数值,例如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
(引用)类型:
那么为什么要尝试针对IntXX
/UIntXX
类型复制此内容?
基本: 好奇心,是由于最近看到一些问题引起的,这些问题涉及为何Int
值类型似乎可以用AnyObject
(参考)变量,而例如Int64
,不能;这自然可以由上面提到的桥接来解释.挑选一些:
Int64
,UInt16
等实际实现这种自动桥接至AnyObject
(NSNumber
)的可能性.这些线程中的答案(正确地)集中在解释AnyObject
为什么不能保存值类型的原因,以及如何不桥接IntXX
/UIntXX
类型以将其自动转换为基础的基础Foundation类型. 次要: 对于在32位和64位体系结构上运行的应用程序,都有一些狭窄的用例-使用隐式转换为,在某些情况下,例如使用Int32
或Int64
类型优于Int
.一个(有点)这样的例子:
是(可能):通过符合协议_ObjectiveCBridgeable
(以下答案基于使用 Swift 2.2 和XCode 7.3.)
就在我思考是要发布还是只是跳过此问题时,我偶然发现了,特别是协议_ObjectiveCBridgeable
.我之前在 Swiftdoc.org 上已经简短地注意到了该协议,但是它的当前(空白)蓝图形式为后者,我从没想过.但是,使用来自Swift来源的_ObjectiveCBridgeable
蓝图,我们可以迅速让一些自定义类型的本机符合它.
在继续之前,请注意_ObjectiveCBridgeable
是内部/隐藏协议(_UnderScorePreFixedProtocol
),因此在即将发布的Swift版本中,基于该协议的解决方案可能会在没有警告的情况下中断.
启用Int64
桥接至基础类NSNumber
作为一个例子,扩展Int64
以符合_ObjectiveCBridgeable
,然后测试这个非常简单的修复程序是否足以满足从Int64
到基础类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
,UInt32
,(Int64
)和UInt64
桥接到NSNumber
使用下面的NSNumber
转换表,可以轻松地修改Int64
与_ObjectiveCBridgeable
的上述一致性,以涵盖任何Swift-native整数类型.
/* 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 */
Question
- Is it possible to replicate Swifts numeric value bridging to Foundation:s
NSNumber
reference type, for e.g.Int32
,UInt32
,Int64
andUInt64
types? Specifically, replicating the automatic by-assignment bridging covered below.
Intended example usage of such a solution:
let foo : Int64 = 42
let bar : NSNumber = foo
/* Currently, as expected, error:
cannot convert value of type 'Int64' to specified type 'NSNumber */
Background
Some of the native Swift number (value) types can be automatically bridged to NSNumber
(reference) type:
From Interoperability - Working with Cocoa Data Types - Numbers.
So why attempt to replicate this for the IntXX
/UIntXX
types?
Primarily: Curiosity, sparked by seeing some questions recently with underlying problems covering confusion over why an Int
value type seemingly can be represented by an AnyObject
(reference) variable, whereas e.g. Int64
, cannot; which is naturally explained by the bridging covered above. To pick a few:
- Why is a Swift Array<Int> compatible with AnyObject?
- Cannot subscript a value of type '[UInt32]'
- Using generic arrays in swift
None of Q&A:s above mentions, however, the possibility of actually implementing such automatic bridging to AnyObject
(NSNumber
) from the non-bridged types Int64
, UInt16
and so on. The answers in these threads rather focuses (correctly) on explaining why AnyObject
cannot hold value types, and how the IntXX
/UIntXX
types are not bridged for automatic conversion to the underlying Foundation types of the former.
Secondarily: For applications running at both 32-bit and 64-bit architectures, there are some narrow use cases—using Swift native number types implicitly converted to AnyObject
, in some context—where using e.g. Int32
or Int64
type would be preferred over Int
. One (somewhat) such example:
Yes (it's possible): by conformance to protocol _ObjectiveCBridgeable
(The following answer is based on using Swift 2.2 and XCode 7.3.)
Just as I was pondering over whether to post or simply skip this question, I stumbled over swift/stdlib/public/core/BridgeObjectiveC.swift
in the Swift source code, specifically the protocol _ObjectiveCBridgeable
. I've briefly noticed the protocol previously at Swiftdoc.org, but in its current (empty) blueprint form in the latter, I've never given much thought to it. Using the blueprints for _ObjectiveCBridgeable
from the Swift source we can, however, swiftly let some native of custom type conform to it.
Before proceeding, note that _ObjectiveCBridgeable
is an internal/hidden protocol (_UnderScorePreFixedProtocol
), so solutions based on it might break without warning in upcoming Swift versions.
Enabling Int64
bridging to Foundation class NSNumber
As an example, extend Int64
to conform to _ObjectiveCBridgeable
, and subsequently test if this quite simple fix is sufficient for the implicit type conversion (bridging) from Int64
to Foundation class NSNumber
holds.
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:
/* 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
Hence, conformance to _ObjectiveCBridgeable
is indeed sufficient to enable automatic by-assignment bridging to the corresponding Foundation class; in this case, NSNumber
(in Swift, __NSCFNumber
).
Enabling Int8
, UInt8
, Int16
, UInt16
, Int32
, UInt32
, (Int64
), and UInt64
bridging to NSNumber
The above conformance of Int64
to _ObjectiveCBridgeable
can easily be modified to cover any of the Swift-native integer types, using the NSNumber
conversion table below.
/* 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 */
这篇关于是否可以将(U)Int8/16/32/64类型的Swifts自动数值桥接到Foundation(NSNumber)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!