Может быть установлен метод Swift для расширений по протоколам, доступным в Objective-c
Можно ли вызывать методы, определенные в расширении протокола в 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 для" IceCream "объявляет селектор...".
В моей конфигурации, если метод определен непосредственно в реализации объекта 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)"
}
}
}
Ответы
Ответ 1
Я почти уверен, что ответ на этот вопрос "нет", хотя я не нашел официальную документацию Apple, в которой говорится об этом.
Вот сообщение из списка рассылки быстрой эволюции, в котором обсуждается предложение использовать динамическую отправку для всех вызовов методов, что обеспечило бы семантику вызова более похожей на Objective-C:
Опять же, единственным исключением является расширение протокола. В отличие от любой другой конструкции на языке, методы расширения протокола отправляются статически в ситуации, когда виртуальная отправка приведет к разным результатам. Ошибка компилятора предотвращает это несоответствие. (https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001707.html)
Расширения протокола - это функция языка только для Swift и, как таковая, не видны objc_msgSend()
.
Ответ 2
Если вы можете удалить 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, однако вы можете расширить класс в swift как обходной путь.
@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 ?? "")"
}
}
}
Этот код не чист вообще, но он работает.