【问题标题】:Conversion operator in swiftswift中的转换运算符
【发布时间】:2016-07-20 09:51:24
【问题描述】:

是否可以在swift 中编写自定义转换(强制转换)运算符?特别是我正在寻找枚举转换,例如:

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

当然,我可以通过显式方法转换为String,但我希望有隐式机制。

【问题讨论】:

  • 不,自定义转换方法很久以前就从 Swift 中删除了。
  • @MartinR,带有MyEnum 输入参数的String 初始化器呢?
  • 他不能将枚举设为字符串类型并使用rawValue吗?
  • @Feldur 我相信这个问题的根源不是如何修改给定枚举的特定 rawValue 类型,而是潜在的转换(理想情况下,对于 OP:隐式)到其他给定类型(与rawValue 的类型不同)。

标签: swift enums type-conversion typecast-operator


【解决方案1】:

免责声明/TL;DR!这个答案与技术问题有关,即我们是否可以在我们自己的不同 Swift 类型之间实现隐式桥接机制。答案是:在某些情况下,是的,但仅在有限的意义上和通过“黑客”的方式:不要使用这是生产代码!

Swift 内部协议滥用:我们可能会为 Obj-C 对象实现隐式机制(例如NSNumberNSString ...)

正如 MartinR 在他的评论中所写,(本机)Swift 不存在自定义转换方法。

对于技术讨论,但是,我们可以(ab)使用内部协议_ObjectiveCBridgeable 来允许从您的枚举到 Obj-C 对象的隐式桥接,在这种情况下,例如NSString。内部协议_ObjectiveCBridgeable主题更详细的问答,见

在继续之前,我将在上面的帖子中引用我的回答中的免责声明

...注意_ObjectiveCBridgeable 是一个内部/隐藏协议 (_UnderScorePreFixedProtocol),因此基于它的解决方案可能会中断 在即将发布的 Swift 版本中没有警告。


示例 #1:实现枚举到 NSString 的隐式桥接

首先让我们为您的枚举添加一个可失败的初始化程序,允许String 实例进行(尝试)初始化:

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
        }
    }
}

接下来,让MyEnum符合_ObjectiveCBridgeable,如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
    }
}

有了上面的一致性,我们现在可以使用从 MyEnum 实例到 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

示例 #2:实现枚举到自定义 Swift 本机类型的隐式桥接

我们甚至可能进一步滥用_ObjectiveCBridgeable 协议,使用它的(深后端)机制来实现两个原生 Swift 类型之间的隐式桥接,限制是桥接的类型 to 必须是引用类型(特别是:该类型的实例必须可以由AnyObject 表示,因此存在引用类型限制)。

MyEnum 与上面的定义相同,但另外,定义一个引用(类)类型Foo,并将MyEnum_ObjectiveCBridgeable 与桥接to type 一致,@987654345 @被设置为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
    }
}

我们现在可以使用从 MyEnum 实例到 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

最后请注意,对于任何给定类型(例如,MyEnum),您自然只能实现到单个其他(引用)类型的隐式桥接;因为您只能符合 _ObjectiveCType 一次(对于 typealias _ObjectiveCType 的唯一类型),否则会产生冗余协议一致性的编译时错误。


以上内容针对 Swift 2.2 进行了测试。

【讨论】:

  • 感谢您提供如此详细的解释。遗憾的是这是一种黑客方式,但仍然很有帮助。
  • @brigadir 很高兴深入探讨这个主题!但同样,正如您所写的,这些都是技巧,不应该真正在实践中使用,即使与它们一起玩会很有趣:)
猜你喜欢
  • 2015-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
  • 2012-01-27
相关资源
最近更新 更多