如何在Swift协议中定义可选方法?

时间:2014-06-04 08:41:52

标签: ios swift swift-protocols swift-extensions

Swift有可能吗?如果没有,那么有解决方法吗?

19 个答案:

答案 0 :(得分:424)

1。使用默认实现(首选)。

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 */
            }
        }
    }
    

    没有办法实现这样的优化 - 你不知道你的委托是否实现了一个方法。

    虽然有许多不同的方法可以解决这个问题(使用可选的闭包,不同的代理对象,但不同的操作仅举几例),这个例子清楚地表明了这个问题。


2。使用@objc optional

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

class MyClass: NSObject, MyProtocol {
    /* no compile error */
}

<强>优点

  • 不需要默认实现。您只需声明一个可选方法或变量就可以了。

<强>缺点

  • 通过要求所有符合要求的类型与Objective-C兼容,严重限制了协议的功能。这意味着,只有从NSObject继承的类才能符合此类协议。没有结构,没有枚举,没有相关的类型。

  • 您必须始终通过可选地调用或检查符合类型是否实现它来检查是否实现了可选方法。如果你经常调用可选方法,这可能会引入很多样板。

答案 1 :(得分:383)

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

答案 2 :(得分:36)

由于有关如何使用可选修饰符 @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

答案 3 :(得分:32)

此处涉及将协议标记为“@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

答案 4 :(得分:32)

以下是委托模式的具体示例。

设置您的协议:

@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()
    {
    }
}

一个重要的事情是可选方法是可选的,需要一个&#34;?&#34;在打电话时。提到第二个问号。

delegate?.optionalMethod?()

答案 5 :(得分:31)

Swift 3.0

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

这会节省你的时间。

答案 6 :(得分:26)

  • 您需要在每种方法之前添加optional关键字。
  • 但请注意,要使其正常工作,您的协议必须使用@objc属性进行标记。
  • 这进一步暗示该协议适用于类而非结构。

答案 7 :(得分:11)

具有协议继承的纯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()
    }
}

答案 8 :(得分:10)

为了说明安托万答案的机制:

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 :(得分:8)

我认为在询问如何之前你可以实现一个可选的协议方法,你应该问为什么你应该实现一个。

如果我们将swift协议视为经典面向对象编程中的接口,可选方法没有多大意义,也许更好的解决方案是创建默认实现,或将协议分成一组协议(可能在它们之间有一些继承关系)来表示协议中方法的可能组合。

如需进一步阅读,请参阅https://useyourloaf.com/blog/swift-optional-protocol-methods/,其中提供了有关此问题的精彩概述。

答案 10 :(得分:7)

从原始问题略微偏离主题,但它建立了Antoine的想法,我认为它可能会帮助某人。

对于具有协议扩展名的结构,您还可以使计算属性可选。

您可以在协议选项

上创建一个属性
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?
}

我还写了如何做optional properties in Swift protocols on my blog,我将不断更新,以防万一通过Swift 2版本发生变化。

答案 11 :(得分:4)

有两种方法可以在Swift协议中创建可选方法。

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具有一项称为扩展的功能,该功能使我们能够为那些我们希望是可选的方法提供默认实现。

答案 12 :(得分:3)

如果你想在纯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()
    }
}

然后你可以实现协议而不定义每个 func

答案 13 :(得分:3)

这里只是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!)
    }

 }

答案 14 :(得分:2)

如何创建可选的和必需的委托方法。

@objc protocol InterViewDelegate:class {

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

}

答案 15 :(得分:0)

一种选择是将它们存储为可选函数变量:

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

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

答案 16 :(得分:-1)

要快速定义Optional Protocol,应在该协议内的@objc声明和Protocol / attribute声明之前使用method关键字。 以下是协议的“可选属性”的示例。

@objc protocol Protocol {

  @objc optional var name:String?

}

class MyClass: Protocol {

   // No error

}

答案 17 :(得分:-1)

在协议中定义功能并为该协议创建扩展,然后为要用作可选功能的功能创建空实现。

答案 18 :(得分:-19)

@optional放在方法或属性的前面。