Как определить дополнительные методы в протоколе Swift?

Возможно ли это в Swift? Если нет, то есть ли это обходное решение?

Ответ 1

1. Использование реализаций по умолчанию (предпочтительно).

protocol MyProtocol {
    func doSomething()
}

extension MyProtocol {
    func doSomething() {
        /* return a default value or just leave empty */
    }
}

struct MyStruct: MyProtocol {
    /* no compile error */
}
Преимущества

Advantages

  • Нет времени выполнения Objective C (ну, по крайней мере, явно). Это означает, что вы можете соответствовать структурам, перечислениям и классам non- NSObject. Кроме того, это означает, что вы можете воспользоваться мощной системой генериков.

  • Вы всегда можете быть уверены, что все требования выполнены при обнаружении типов, соответствующих такому протоколу. Это всегда либо конкретная реализация, либо реализация по умолчанию. Так ведут себя "интерфейсы" или "контракты" на других языках.

Недостатки

  • Для требований non- 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 */
            }
        }
    }
    

    Нет способа реализовать такую оптимизацию - вы не можете знать, реализует ли ваш делегат метод или нет.

    Несмотря на то, что существует ряд различных способов преодоления этой проблемы (использование необязательных замыканий, разные объекты-делегаты для разных операций и многие другие), этот пример ясно представляет проблему.


2. Использование @objc optional.

@objc protocol MyProtocol {
    @objc optional func doSomething()
}

class MyClass: NSObject, MyProtocol {
    /* no compile error */
}
Преимущества

Advantages

  • Реализация по умолчанию не требуется. Вы просто объявляете необязательный метод или переменную и готовы к работе.

Недостатки

  • Это серьезно ограничивает возможности вашего протокола, требуя, чтобы все соответствующие типы были совместимы с Objective-C. Это означает, что только классы, которые наследуются от NSObject, могут соответствовать такому протоколу. Нет структур, нет перечислений, нет связанных типов.

  • Вы всегда должны проверять, реализован ли необязательный метод, либо вызывая его, либо проверяя, реализует ли его соответствующий тип. Это может привести к множеству шаблонов, если вы часто вызываете дополнительные методы.

Ответ 2

В 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/

Ответ 3

Поскольку есть несколько ответов о том, как использовать необязательный модификатор и атрибут @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

Ответ 4

Вот конкретный пример с шаблоном делегирования.

Настройка протокола:

@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?()

Ответ 5

Другие ответы здесь, связанные с маркировкой протокола как "@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"

Чтобы объявить факультативные протоколы, которые хорошо работают с быстрым, объявите функции как переменные вместо 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

Ответ 6

В Swift 3.0

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}

Это сэкономит ваше время.

Ответ 7

  • Вам нужно добавить ключевое слово optional перед каждым методом.
  • Обратите внимание, однако, что для этого, ваш протокол должен быть помечен атрибутом @objc.
  • Это также подразумевает, что этот протокол применим к классам, но не к структурам.

Ответ 8

Чтобы проиллюстрировать механику Антуана, ответьте:

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"

Ответ 9

Чистый подход Swift с наследованием протокола:

//Required methods
protocol MyProtocol {
    func foo()
}

//Optional methods
protocol MyExtendedProtocol: MyProtocol {
    func bar()
}

class MyClass {
    var delegate: MyProtocol
    func myMethod() {
        (delegate as? MyExtendedProtocol).bar()
    }
}

Ответ 10

Я думаю, что прежде чем спросить, как вы можете реализовать необязательный метод протокола, вы должны спросить, почему вы должны его реализовать.

Если мы рассматриваем быстрые протоколы как интерфейс в классическом объектно-ориентированном программировании, дополнительные методы не имеют особого смысла, и, возможно, лучшим решением будет создание реализации по умолчанию или разделение протокола на набор протоколов (возможно, с некоторыми отношениями наследования). между ними) для представления возможной комбинации методов в протоколе.

Для дальнейшего чтения см. Https://useyourloaf.com/blog/swift-optional-protocol-methods/, в котором содержится отличный обзор по этому вопросу.

Ответ 11

Немного не по теме от первоначального вопроса, но он основывается на идее Антуана, и я подумал, что это может кому-то помочь.

Вы также можете сделать вычисляемые свойства необязательными для структур с расширениями протокола.

Вы можете сделать свойство по протоколу необязательным

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.

Ответ 12

Как создать необязательные и обязательные методы делегата.

@objc protocol InterViewDelegate:class {

    @objc optional func optfunc()  //    This is optional
    func requiredfunc()//     This is required 

}

Ответ 13

Здесь очень простой пример для быстрых классов ТОЛЬКО, а не для структур или перечислений. Обратите внимание, что метод протокола, являющийся факультативным, имеет два уровня опциональной цепочки при игре. Также классу, принимающему протокол, требуется атрибут @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!)
    }

 }

Ответ 14

если вы хотите сделать это в чистом быстром, лучший способ - реализовать реализацию по умолчанию, если вы вернете тип 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()
    }
}

тогда вы можете реализовать протокол, не определяя каждый func

Ответ 15

Один из вариантов - сохранить их как необязательные переменные функции:

struct MyAwesomeStruct {
    var myWonderfulFunction : Optional<(Int) -> Int> = nil
}

let squareCalculator =
    MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)

Ответ 16

Чтобы определить Optional Protocol в swift, вы должны использовать @objc слово @objc перед объявлением Protocol объявлением attribute/method внутри этого протокола. Ниже приведен пример необязательного свойства протокола.

@objc protocol Protocol {

  @objc optional var name:String?

}

class MyClass: Protocol {

   // No error

}

Ответ 17

Определите функцию в протоколе и создайте расширение для этого протокола, затем создайте пустую реализацию для функции, которую вы хотите использовать как опциональную.

Ответ 18

Поместите @optional перед методами или свойствами.