一尘不染

是否可以将(U)Int8 / 16/32/64类型的Swifts自动数值桥接到Foundation(NSNumber)?

swift

  • 是否有可能重复雨燕数值桥接基金会:■ NSNumber引用类型,例如Int32UInt32Int64UInt64类型?具体来说,复制下面介绍的自动预分配桥接。

这种解决方案的预期用法示例:

let foo : Int64 = 42
let bar : NSNumber = foo
    /* Currently, as expected, error:
       cannot convert value of type 'Int64' to specified type 'NSNumber */

背景

一些本机Swift数(值)类型可以自动桥接到NSNumber(引用)类型:

迅数字结构类型的实例,例如IntUIntFloatDouble,和Bool,不能由所表示的
AnyObject类型,因为AnyObject仅代表一个类类型的实例。但是,Foundation启用桥接到时,可以将Swift数值分配给常量和AnyObject类型变量,
作为 类的 桥接实例NSNumber

Swift会自动将某些本机数字类型(例如Int
和)桥Float接到NSNumber。通过这种桥接,您可以NSNumber从以下类型之一创建 :

let n = 42
let m: NSNumber = n

它还允许您将类型的值传递Int给例如 期望为的参数NSNumber。…

以下所有类型都会自动桥接到NSNumber:

- Int
- UInt
- Float
- Double
- Bool

互操作性出发-使用可可数据类型-
数字

那么,为什么要尝试为IntXX/ UIntXX类型复制呢?

主要是:
好奇心,这是由于最近看到一些问题而引起的,这些问题涉及一些困惑,这些困惑涵盖了为什么一个Int值类型似乎可以由一个AnyObject(引用)变量表示,而例如Int64,不能;这自然可以通过上面提到的桥接来解释。

上面的问答均未提及从非桥接类型实际实现到AnyObjectNSNumber)的这种自动桥接的可能性Int64UInt16依此类推。这些线程中的答案(正确地)集中在解释为什么AnyObject不能保存值类型,以及如何不桥接IntXX/
UIntXX类型以将其自动转换为基础的基础Foundation类型。

其次:
对于同时在32位和64位体系结构上运行的应用程序,存在一些狭窄的用例-AnyObject在某些情况下使用隐式转换为的Swift本机数字类型-
在使用egInt32Int64type优先Int。一个(有点)这样的例子:


阅读 308

收藏
2020-07-07

共1个答案

一尘不染

是(可能):通过遵守协议 _ObjectiveCBridgeable

(以下答案基于使用 Swift 2.2 和XCode 7.3的情况。)

正如我在考虑是发布还是跳过该问题时,我偶然发现了swift/stdlib/public/core/BridgeObjectiveC.swiftSwift源代码,尤其是协议_ObjectiveCBridgeable。我之前在Swiftdoc.org上已经简短地注意到了该协议,但是在后者的当前(空)蓝图形式中,我从没有对此进行过多考虑。_ObjectiveCBridgeable但是,使用来自Swift来源的蓝图,我们可以迅速让一些自定义类型的本机遵循它。

在继续之前,请注意这_ObjectiveCBridgeable是一个内部/隐藏协议(_UnderScorePreFixedProtocol),因此在即将发布的Swift版本中,基于该协议的解决方案可能会在没有警告的情况下中断。


启用Int64桥接至基金会类NSNumber

作为一个示例,扩展Int64以符合_ObjectiveCBridgeable,然后测试此非常简单的修复程序是否足以将隐式类型转换(桥接)从Int64Foundation类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)。


启用Int8UInt8Int16UInt16Int32UInt32,(
Int64)和UInt64桥接NSNumber

使用下面的转换表Int64_ObjectiveCBridgeable可以轻松地修改to
的上述一致性,以涵盖任何Swiftn本地整数类型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              */
2020-07-07