22

是否可以从 Objective-C 调用 Swift 协议扩展中定义的方法?

例如:

protocol Product {
    var price:Int { get }
    var priceString:String { get }
}

extension Product {
    var priceString:String {
        get {
            return "$\(price)"
        }
    }
}

class IceCream : Product {
    var price:Int {
        get {
            return 2
        }
    }
}

一个实例的价格字符串IceCream是 '$2' 并且可以在 Swift 中访问,但是该方法在 Objective-C 中不可见。编译器抛出错误“'IceCream' 的无可见 @interface 声明选择器 ...”。

在我的配置中,如果方法是直接在 Swift 对象的实现中定义的,那么一切都会按预期进行。IE:

protocol Product {
    var price:Int { get }
    var priceString:String { get }
}

class IceCream : Product {
    var price:Int {
        get {
            return 2
        }
    }
    var priceString:String {
        get {
            return "$\(price)"
        }
    }
}
4

3 回答 3

16

我几乎可以肯定这个问题的答案是“不”,尽管我还没有找到说明它的官方 Apple 文档。

这是来自 swift-evolution 邮件列表的一条消息,讨论了对所有方法调用使用动态调度的提议,这将提供更像 Objective-C 的调用语义:

同样,唯一的例外是协议扩展。与该语言中的任何其他构造不同,协议扩展方法是在虚拟分派会导致不同结果的情况下静态分派的。没有编译器错误可以防止这种不匹配。(https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001707.html

协议扩展是 Swift 独有的语言功能,因此对objc_msgSend().

于 2016-01-27T20:52:25.970 回答
3

如果可以priceString从协议中删除 ,并且只在您的扩展中使用它,则可以通过转换IceCreamProduct辅助扩展中的 a 来调用协议扩展。

@objc protocol Product {
    var price:Int { get }
}

extension Product {
    var priceString:String {
        return "$\(price)"
    }
}

// This is the trick
// Helper extension to be able to call protocol extension from obj-c
extension IceCream : Product {
    var priceString:String {
        return (self as Product).priceString
    }
}

@objc class IceCream: NSObject {
    var price: Int {
        return 2
    }
}
于 2017-02-07T15:19:15.943 回答
2

协议扩展不适用于 @objc 协议,但是您可以快速扩展该类作为解决方法。

@objc protocol Product {
   var price: NSNumber? { get }
   var priceString:String { get }
}

...
// IceCream defined in Objective-C that does not extend Product
// but has @property(nonatomic, strong, nullable) (NSNumber*)price
// to make it confirm to Product
...
extension IceCream: Product {
   var priceString:String {
      get {
        return "$\(price ?? "")"
      }
   }
}

这段代码一点也不干净,但它可以工作。

于 2016-12-15T04:02:13.880 回答