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


возможно ли это в Swift? Если нет, есть ли обходной путь для этого?

15 279

15 ответов:

Если вы хотите использовать дополнительные методы, вы должны отметить свой протокол с :

@objc protocol MyProtocol {

    @objc optional func doSomething()

}

class MyClass : MyProtocol {

    // no error

}

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

в Swift 2 и далее можно добавить реализации протокола по умолчанию. Это создает новый способ дополнительных методов в протоколах.

protocol MyProtocol {
    func doSomethingNonOptionalMethod()
    func doSomethingOptionalMethod()
}

extension MyProtocol {
    func doSomethingOptionalMethod(){ 
        // leaving this empty 
    }
}

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

Я написал небольшое резюме здесь: http://www.avanderlee.com/swift-2-0/optional-protocol-methods/

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

In Swift 3.0

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

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

вот конкретный пример с узором делегации.

настройки протокола:

@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?()
  • вам нужно добавить optional ключевое слово перед каждым методом.
  • обратите внимание, однако, что для этого, чтобы работать,ваш протокол должен быть отмечен атрибутом @objc.
  • это также означает, что этот протокол будет применим к классам, но не к структурам.

Так как есть некоторые ответы о том, как использовать необязательный модификатор и @objc атрибут чтобы определить дополнительный протокол требований, я приведу пример о том, как использовать расширения протокола define optional protocol.

ниже код 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

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

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"

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

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

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

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

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

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

вы можете сделать свойство по Факультативному протоколу

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 релизы.

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

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

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

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

 }

Если вы хотите сделать это в чистом swift, лучший способ-предоставить реализацию по умолчанию particulary, если вы возвращаете тип Swift, например struct С быстрыми типами

пример :

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

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

@objc protocol Protocol {

  @objc optional var name:String?

}

class MyClass: Protocol {

   // No error

}

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