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

239

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

Теги:
swift-extensions
swift-protocols

13 ответов

287
Лучший ответ

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

@objc protocol MyProtocol {

    @objc optional func doSomething()

}

class MyClass : MyProtocol {

    // no error

}

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

  • 16
    Посмотрите на улучшенный способ дополнительных методов в Swift, используя расширение (ниже)
  • 1
    И как можно проверить поддержку дополнительного метода протокола в экземпляре? respondsToSelector ?
Показать ещё 11 комментариев
283

В Swift 2.0 можно добавлять стандартные реализации протокола. Это создает новый способ использования дополнительных методов в протоколах.

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

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

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

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

  • 2
    Это, вероятно, самый чистый способ сделать это в Swift. Жаль, что это не работает до Swift 2.0.
  • 12
    @ MattQuiros Я обнаружил, что вам действительно нужно объявить функцию в определении протокола, в противном случае функция расширения без операции не будет переопределена в ваших классах, которые соответствуют протоколу.
Показать ещё 7 комментариев
28

Другие ответы здесь, связанные с маркировкой протокола как "@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
  • 3
    С этим решением вы не можете получить доступ к себе из любой функции протокола. Это может вызвать проблемы в некоторых случаях!
  • 1
    @ GeorgeGreen Вы можете получить доступ к себе. Отметьте переменную функции как ленивую и используйте список захвата внутри замыкания .
Показать ещё 3 комментария
23

В Swift 3.0

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

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

  • 6
    Любой источник того, почему @objc нужен всем участникам сейчас?
  • 0
    @ Gautam Sareriya: Как вы думаете, что лучше всего подходит этот подход или создание пустых методов расширения?
Показать ещё 1 комментарий
23
  • Вам нужно добавить ключевое слово optional перед каждым методом.
  • Обратите внимание, однако, что для этого, ваш протокол должен быть помечен атрибутом @objc.
  • Это также подразумевает, что этот протокол применим к классам, но не к структурам.
  • 8
    @ Downvoter: почему голосование против? выглядит звук
  • 0
    Незначительная коррекция (слишком мала для редактирования!), Но она должна быть «необязательной», а не «@optional»
Показать ещё 1 комментарий
19

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

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

@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?()
  • 2
    Это должен быть правильный ответ. Просто, чисто и понятно.
  • 0
    Я согласен, этот ответ короткий, сладкий и прямо к делу. Спасибо!
7

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

  • 1
    Хорошая работа! Это должен быть правильный ответ, так как он решает проблему без вовлечения Objective C.
  • 0
    Это гораздо более быстрый, чем принятый ответ.
Показать ещё 1 комментарий
4

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

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

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

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?
}

Ive также написала, как сделать дополнительные свойства в протоколах Swift в моем блоге, которые Ill продолжают обновлять, если все изменится через Swift 2 релизов.

3

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

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

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

2

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

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"
2

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

2

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

 }
  • 0
    Можете ли вы описать немного больше « двух уровней необязательной игры », а именно: delegate?.indexDidChange?(index!) ?
  • 2
    Если бы мы написали в протоколе такой необязательный метод, как этот: protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } то вы бы delegate?.indexDidChange(index!) его без знака вопроса: delegate?.indexDidChange(index!) Когда вы устанавливаете необязательное требование для метода в протоколе Тип, который будет соответствовать ему, может НЕ реализовывать этот метод, так что ? используется для проверки реализации, и если ее нет, программа не будет аварийно завершена. @Unheilig
Показать ещё 3 комментария
-15

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню