Возможно ли это в Swift? Если нет, то есть ли обходной путь для этого?
Возможно ли это в Swift? Если нет, то есть ли обходной путь для этого?
Ответы:
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: MyProtocol {
/* no compile error */
}
преимущества
Нет времени выполнения Objective C (ну, по крайней мере, явно). Это означает, что вы можете соответствовать структурам, перечислениям и не NSObject
классам. Кроме того, это означает, что вы можете воспользоваться мощной системой генериков.
Вы всегда можете быть уверены, что все требования выполняются при обнаружении типов, соответствующих такому протоколу. Это всегда либо конкретная реализация, либо реализация по умолчанию. Так ведут себя «интерфейсы» или «контракты» на других языках.
Недостатки
В случае отсутствия Void
требований вам необходимо иметь разумное значение по умолчанию , что не всегда возможно. Однако, когда вы сталкиваетесь с этой проблемой, это означает, что либо у такого требования действительно не должно быть реализации по умолчанию, либо вы допустили ошибку при разработке API.
Вы не можете отличить реализацию по умолчанию от реализации вообще , по крайней мере без решения этой проблемы с помощью специальных возвращаемых значений. Рассмотрим следующий пример:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}
Если вы предоставляете реализацию по умолчанию, которая просто возвращает true
- это нормально на первый взгляд. Теперь рассмотрим следующий псевдокод:
final class SomeParser {
func parse(data: Data) -> [Any] {
if /* delegate.validate(value:) is not implemented */ {
/* parse very fast without validating */
} else {
/* parse and validate every value */
}
}
}
Нет способа реализовать такую оптимизацию - вы не можете знать, реализует ли ваш делегат метод или нет.
Несмотря на то, что существует ряд различных способов преодоления этой проблемы (использование необязательных замыканий, разные объекты-делегаты для разных операций и многие другие), этот пример ясно представляет проблему.
@objc optional
.@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
преимущества
Недостатки
Это серьезно ограничивает возможности вашего протокола, требуя совместимости всех соответствующих типов с Objective-C. Это означает, что только классы, которые наследуются, NSObject
могут соответствовать такому протоколу. Нет структур, нет перечислений, нет связанных типов.
Вы всегда должны проверять, реализован ли необязательный метод , либо вызывая его, либо проверяя, реализует ли его соответствующий тип. Это может привести к множеству шаблонов, если вы часто вызываете дополнительные методы.
respondsToSelector
?
optional func doSomething(param: Int?)
В Swift 2 и далее можно добавлять реализации протокола по умолчанию. Это создает новый способ дополнительных методов в протоколах.
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
func doSomethingOptionalMethod(){
// leaving this empty
}
}
Это не очень хороший способ создания дополнительных методов протокола, но дает вам возможность использовать структуры в обратных вызовах протокола.
Я написал небольшое резюме здесь: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
Так как есть несколько ответов о том, как использовать необязательный модификатор и атрибут @objc для определения необязательного протокола требований, я приведу пример того, как использовать расширения протокола для определения необязательного протокола.
Ниже код Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocol Cancelable {
/// default implementation is empty.
func cancel()
}
extension Cancelable {
func cancel() {}
}
class Plane: Cancelable {
//Since cancel() have default implementation, that is optional to class Plane
}
let plane = Plane()
plane.cancel()
// Print out *United Airlines can't cancelable*
Обратите внимание, что методы расширения протокола не могут быть вызваны кодом Objective-C, и хуже всего то, что команда Swift не исправит это. https://bugs.swift.org/browse/SR-492
Другие ответы, связанные с пометкой протокола как «@objc», не работают при использовании специфичных для swift типов.
struct Info {
var height: Int
var weight: Int
}
@objc protocol Health {
func isInfoHealthy(info: Info) -> Bool
}
//Error "Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Чтобы объявить необязательные протоколы, которые хорошо работают с swift, объявите функции как переменные вместо func.
protocol Health {
var isInfoHealthy: (Info) -> (Bool)? { get set }
}
А затем реализовать протокол следующим образом
class Human: Health {
var isInfoHealthy: (Info) -> (Bool)? = { info in
if info.weight < 200 && info.height > 72 {
return true
}
return false
}
//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
Затем вы можете использовать "?" проверить, была ли реализована функция
func returnEntity() -> Health {
return Human()
}
var anEntity: Health = returnEntity()
var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))?
//"isHealthy" is true
Вот конкретный пример с шаблоном делегирования.
Настройте свой протокол:
@objc protocol MyProtocol:class
{
func requiredMethod()
optional func optionalMethod()
}
class MyClass: NSObject
{
weak var delegate:MyProtocol?
func callDelegate()
{
delegate?.requiredMethod()
delegate?.optionalMethod?()
}
}
Установите делегата для класса и реализуйте протокол. Смотрите, что необязательный метод не должен быть реализован.
class AnotherClass: NSObject, MyProtocol
{
init()
{
super.init()
let myInstance = MyClass()
myInstance.delegate = self
}
func requiredMethod()
{
}
}
Одна важная вещь заключается в том, что дополнительный метод является необязательным и требует "?" при звонке. Упомяните второй знак вопроса.
delegate?.optionalMethod?()
В Swift 3.0
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
Это сэкономит ваше время.
@objc
сейчас нужен всем членам?
required
флагом, но с ошибками: required
может использоваться только в объявлениях 'init'.
optional
ключевое слово перед каждым методом.@objc
не только протокол.
Чтобы проиллюстрировать механику ответа Антуана:
protocol SomeProtocol {
func aMethod()
}
extension SomeProtocol {
func aMethod() {
print("extensionImplementation")
}
}
class protocolImplementingObject: SomeProtocol {
}
class protocolImplementingMethodOverridingObject: SomeProtocol {
func aMethod() {
print("classImplementation")
}
}
let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()
noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"
Я думаю, что прежде чем спросить, как вы можете реализовать дополнительный метод протокола, вы должны спросить почему вы должны его реализовать.
Если мы думаем о быстрых протоколах как интерфейсе в классическом объектно-ориентированном программировании, дополнительные методы не имеют особого смысла, и, возможно, лучшим решением будет создание реализации по умолчанию или разделение протокола на набор протоколов (возможно, с некоторыми отношениями наследования). между ними) для представления возможной комбинации методов в протоколе.
Для дальнейшего чтения см. Https://useyourloaf.com/blog/swift-optional-protocol-methods/ , в котором содержится отличный обзор по этому вопросу.
Немного не по теме из первоначального вопроса, но он основывается на идее Антуана, и я подумал, что это может кому-то помочь.
Вы также можете сделать вычисляемые свойства необязательными для структур с расширениями протокола.
Вы можете сделать свойство по протоколу необязательным
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
Реализовать фиктивное свойство в расширении протокола
extension SomeProtocol {
var optional: String? { return nil }
}
И теперь вы можете использовать структуры, в которых реализовано или не реализовано необязательное свойство.
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
Я также написал в своем блоге , как настраивать необязательные свойства в протоколах Swift , и я буду постоянно обновлять их в случае изменений в выпусках Swift 2.
Как создать необязательные и обязательные методы делегата.
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}
Вот очень простой пример ТОЛЬКО для быстрых классов, а не для структур или перечислений. Обратите внимание, что метод протокола, являющийся необязательным, имеет два уровня необязательной цепочки при воспроизведении. Также классу, принимающему протокол, необходим атрибут @objc в своем объявлении.
@objc protocol CollectionOfDataDelegate{
optional func indexDidChange(index: Int)
}
@objc class RootView: CollectionOfDataDelegate{
var data = CollectionOfData()
init(){
data.delegate = self
data.indexIsNow()
}
func indexDidChange(index: Int) {
println("The index is currently: \(index)")
}
}
class CollectionOfData{
var index : Int?
weak var delegate : CollectionOfDataDelegate?
func indexIsNow(){
index = 23
delegate?.indexDidChange?(index!)
}
}
delegate?.indexDidChange?(index!)
?
protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
тогда вы бы вызвали его без вопросительного знака: delegate?.indexDidChange(index!)
когда вы устанавливаете необязательное требование для метода в протоколе, тип, который будет соответствовать ему, может НЕ реализовывать этот метод , поэтому ?
используется для проверки реализации, и если ее нет, программа не будет аварийно завершена. @Unheilig
weak var delegate : CollectionOfDataDelegate?
(обеспечить слабую ссылку?)
delegate?
использования в свой ответ? Эта информация должна действительно принадлежать другим в будущем. Я хочу выразить это, но эта информация действительно должна быть в ответе.
если вы хотите сделать это в чистом swift, лучшим способом будет предоставить реализацию по умолчанию, если вы возвращаете тип Swift, например, struct с типами Swift
пример :
struct magicDatas {
var damagePoints : Int?
var manaPoints : Int?
}
protocol magicCastDelegate {
func castFire() -> magicDatas
func castIce() -> magicDatas
}
extension magicCastDelegate {
func castFire() -> magicDatas {
return magicDatas()
}
func castIce() -> magicDatas {
return magicDatas()
}
}
тогда вы можете реализовать протокол без определения каждой функции
Существует два способа создания необязательного метода в быстром протоколе.
1 - Первый вариант - пометить ваш протокол с помощью атрибута @objc. Хотя это означает, что он может быть принят только классами, это означает, что вы помечаете отдельные методы как необязательные, например:
@objc protocol MyProtocol {
@objc optional func optionalMethod()
}
2 - более быстрый способ: этот вариант лучше. Напишите реализации по умолчанию необязательных методов, которые ничего не делают, как это.
protocol MyProtocol {
func optionalMethod()
func notOptionalMethod()
}
extension MyProtocol {
func optionalMethod() {
//this is a empty implementation to allow this method to be optional
}
}
Swift имеет функцию под названием extension, которая позволяет нам предоставлять реализацию по умолчанию для тех методов, которые мы хотим использовать не обязательно.
Один из вариантов - сохранить их как необязательные переменные функции:
struct MyAwesomeStruct {
var myWonderfulFunction : Optional<(Int) -> Int> = nil
}
let squareCalculator =
MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
Определите функцию в протоколе и создайте расширение для этого протокола, затем создайте пустую реализацию для функции, которую вы хотите использовать как опциональную.
Для определения Optional
Protocol
в swift вы должны использовать @objc
ключевое слово перед Protocol
объявлением и attribute
/ method
объявление внутри этого протокола. Ниже приведен пример необязательного свойства протокола.
@objc protocol Protocol {
@objc optional var name:String?
}
class MyClass: Protocol {
// No error
}
Поместите @optional
перед методами или свойствами.
@optional
даже не правильное ключевое слово. Это так optional
, и вы должны объявить класс и протокол с @objc
атрибутом.