Is it possible to replicate Swifts automatic numeric value bridging to Foundation (NSNumber) for (U)Int8/16/32/64 types?

前端 未结 1 2004
孤独总比滥情好
孤独总比滥情好 2020-11-28 13:28

Question

  • Is it possible to replicate Swifts numeric value bridging to Foundation:s NSNumber reference type, for e.g. Int32, U
相关标签:
1条回答
  • 2020-11-28 13:32

    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              */
    
    0 讨论(0)
提交回复
热议问题