是否可以将(U)Int8/16/32/64类型的Swifts自动数值桥接到Foundation(NSNumber)? [英] Is it possible to replicate Swifts automatic numeric value bridging to Foundation (NSNumber) for (U)Int8/16/32/64 types?

查看:68
本文介绍了是否可以将(U)Int8/16/32/64类型的Swifts自动数值桥接到Foundation(NSNumber)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

  • 是否可以复制桥接到Foundation:s NSNumber引用类型的Swifts数值,例如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(引用)类型:

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

...

Swift自动桥接某些本机数字类型,例如IntFloatNSNumber.通过这种桥接,您可以创建一个 NSNumber来自以下类型之一:

let n = 42
let m: NSNumber = n

它还允许您将Int类型的值(例如)传递给 参数应为NSNumber . ...

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

- Int
- UInt
- Float
- Double
- Bool

来自互操作性-使用可可数据类型-数字.

那么为什么要尝试针对IntXX/UIntXX类型复制此内容?

基本: 好奇心,是由于最近看到一些问题引起的,这些问题涉及为何Int值类型似乎可以用AnyObject(参考)变量,而例如Int64,不能;这自然可以由上面提到的桥接来解释.挑选一些:

但是,上面的Q& A均未提及从非桥接类型Int64UInt16等实际实现这种自动桥接至AnyObject(NSNumber)的可能性.这些线程中的答案(正确地)集中在解释AnyObject为什么不能保存值类型的原因,以及如何不桥接IntXX/UIntXX类型以将其自动转换为基础的基础Foundation类型.

次要: 对于在32位和64位体系结构上运行的应用程序,都有一些狭窄的用例-使用隐式转换为,在某些情况下,例如使用Int32Int64类型优于Int.一个(有点)这样的例子:

解决方案

是(可能):通过符合协议_ObjectiveCBridgeable

(以下答案基于使用 Swift 2.2 和XCode 7.3.)

就在我思考是要发布还是只是跳过此问题时,我偶然发现了 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).


启用Int8UInt8Int16UInt16Int32UInt32,(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 and UInt64 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:

Instances of the Swift numeric structure types, such as Int, UInt, Float, Double, and Bool, cannot be represented by the AnyObject type, because AnyObject only represents instances of a class type. However, when bridging to Foundation is enabled, Swift numeric values can be assigned to constants and variables of AnyObject type as bridged instances of the NSNumber class.

...

Swift automatically bridges certain native number types, such as Int and Float, to NSNumber. This bridging lets you create an NSNumber from one of these types:

let n = 42
let m: NSNumber = n

It also allows you to pass a value of type Int, for example, to an argument expecting an NSNumber. ...

All of the following types are automatically bridged to NSNumber:

- Int
- UInt
- Float
- Double
- Bool

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:

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)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆