Подтвердить что ты не робот

Может быть установлен метод 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)"
        }
    }
}
4b9b3361

Ответ 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 ?? "")"
      }
   }
}

Этот код не чист вообще, но он работает.