Conversion operator in swift

后端 未结 1 1298
刺人心
刺人心 2021-01-14 12:12

Is it possible to write custom conversion (casting) operator in swift? Especially I\'m looking for enums conversion, ex:

enum MyEnum : Int {
            


        
1条回答
  •  夕颜
    夕颜 (楼主)
    2021-01-14 12:50

    Disclaimer/TL;DR! This answer pertains to the technical question as to whether we can possibly implement implicit bridging mechanisms between different Swift types ourself. The answer is: for some cases, yes, but only in a limited sense and by means of "hacks": do not use this is production code!

    Swift internal protocol abuse: we may implement implicit mechanisms to Obj-C objects (e.g. NSNumber, NSString ...)

    As MartinR writes in his comment, custom conversion methods are not present for (native) Swift.

    For the technical discussion we can, however, (ab)use the internal protocol _ObjectiveCBridgeable to allow implicit bridging from your enum to Obj-C objects, in this case e.g. NSString. For a more detailed Q&A of the subject of the internal protocol _ObjectiveCBridgeable, see

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

    Before proceeding, I'll quote a disclaimer from my answer in thread above:

    ... note that _ObjectiveCBridgeable is an internal/hidden protocol (_UnderScorePreFixedProtocol), so solutions based on it might break without warning in upcoming Swift versions.


    Example #1: implementing implicit bridging of your enum to NSString

    First lets add a failable initializer to your enum, allowing (attempted) initialization by String instances:

    import Foundation
    
    enum MyEnum: Int {
        case Case1 = 0
        case Case2
    
        init?(string: String) {
            switch string {
            case "Case 1": self = .Case1
            case "Case 2": self = .Case2
            default: return nil
            }
        }
    }
    

    Next up, let MyEnum conform to _ObjectiveCBridgeable, as described in more detail in the thread linked to above

    extension MyEnum: _ObjectiveCBridgeable {
    
        typealias _ObjectiveCType = NSString
    
        static func _isBridgedToObjectiveC() -> Bool {
            return true
        }
    
        static func _getObjectiveCType() -> Any.Type {
            return _ObjectiveCType.self
        }
    
        func _bridgeToObjectiveC() -> _ObjectiveCType {
            return NSString(string: "Case \(self.rawValue+1)")
        }
    
        static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) {
            result = MyEnum(string: source as String)
        }
    
        static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) -> Bool {
            self._forceBridgeFromObjectiveC(source, result: &result)
            return true
        }
    }
    

    With the conformance above, we can now make use of implicit bridging from MyEnum instances to NSString

    /* example usage */
    var myCase: MyEnum = .Case1
    var enumNSstr: NSString = myCase // MyEnum -> NSString, implicit
    
    print(enumNSstr) // Case 1
    
    enumNSstr = "Case 2"
    
    // NSString -> MyEnum, by type conversion (castable)
    myCase = (enumNSstr as MyEnum) ?? .Case1
    print(myCase) // Case 2
    

    Example #2: implementing implicit bridging of your enum to a custom Swift native type

    We may even abuse the _ObjectiveCBridgeable protocol further, using its (deep backend) mechanisms to implement implicit bridging between two native Swift types, with the limitation that the type bridged to must be a reference type (specifically: instances of the type must be representable by AnyObject, hence the reference type limitation).

    Let MyEnum be as defined above, but additionally, define a reference (class) type Foo, and conform MyEnum to _ObjectiveCBridgeable with the bridged to type, _ObjectiveCType being set to Foo.

    class Foo {
        var bar: String
        init(bar: String) { self.bar = bar }
    }
    
    extension MyEnum: _ObjectiveCBridgeable {
    
        typealias _ObjectiveCType = Foo
    
        static func _isBridgedToObjectiveC() -> Bool {
            return true
        }
    
        static func _getObjectiveCType() -> Any.Type {
            return _ObjectiveCType.self
        }
    
        func _bridgeToObjectiveC() -> _ObjectiveCType {
            return Foo(bar: "Case \(self.rawValue+1)")
        }
    
        static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) {
            result = MyEnum(string: source.bar)
        }
    
        static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) -> Bool {
            self._forceBridgeFromObjectiveC(source, result: &result)
            return true
        }
    }
    

    We can now make use of implicit bridging from MyEnum instances to Foo

    /* example usage */
    var myCase: MyEnum = .Case1
    var myFoo: Foo = myCase // MyEnum -> Foo, implicit
    print(myFoo.bar) // Case 1
    
    myFoo.bar = "Case 2"
    
    // Foo -> MyEnum, by type conversion (castable)
    myCase = (myFoo as? MyEnum) ?? .Case1
    print(myCase) // Case 2
    

    Finally note that you may, for any given type (say, MyEnum), naturally only implement implicit bridging to a single other (reference) type; since you can only conform to _ObjectiveCType once (for a unique type for the typealias _ObjectiveCType), otherwise yielding a compile time error for redundant protocol conformance.


    The above is tested for Swift 2.2.

    0 讨论(0)
提交回复
热议问题