在Objective-C中是否可以访问定义在协议扩展上的Swift方法?

26

在Swift中,能否从Objective-C调用协议扩展中定义的方法?

例如:

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 中无法看到该方法。编译器会抛出错误“No visible @interface for 'IceCream' declares the selector ...”。在我的配置中,如果直接在 Swift 对象的实现中定义该方法,则一切正常运行。例如:

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

值得注意的是,生成的ProductName-Swift.h文件不包括协议扩展中定义的方法,就像它为类的扩展所做的那样。 - Anthony Mattox
似乎答案是否定的? - Wingzero
3个回答

19

我几乎可以确定答案是否定的,尽管我还没有找到官方的苹果文档来解释它。

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

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

协议扩展是仅限于Swift的语言特性,因此对objc_msgSend()不可见。


4

如果从协议中移除 priceString ,并仅在您的扩展中使用它,则可以通过将 IceCream 强制转换为 Product 并在帮助扩展中调用协议扩展来完成。

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

3

协议扩展与@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 ?? "")"
      }
   }
}

这段代码并不是很规范,但它能够正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接