Conversion operator in swift

梦想与她 提交于 2019-12-30 10:39:08

问题


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

enum MyEnum : Int {
    case Case1 = 0
    case Case2
    func __conversion() -> String { // doesn't work since Swift 1.0
        switch self {
        case Case1: return "Case 1"
        case Case2: return "Case 2"
        }
    }
}

let enumStr: String = MyEnum.Case1

Of course, I can convert to String with explicit method, but I would like to have implicit mechanism.


回答1:


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.



来源:https://stackoverflow.com/questions/38477905/conversion-operator-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!