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

Sel*_*vin 340 ios swift swift-extensions swift-protocols

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

aka*_*kyy 464

如果要使用可选方法,则必须使用NSObject属性标记协议:

protocol MyProtocol {
    func doSomething()
}

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

struct MyStruct: MyProtocol {
    /* no compile error */
}
Run Code Online (Sandbox Code Playgroud)

否则,Swift协议的行为类似于其他OO语言上的接口,其中必须实现接口中定义的所有方法.

  • 在swift中使用扩展(下面)查看可选方法的改进方法 (17认同)
  • 当然,多年以后,这个答案肯定是错误的**.今天在Swift中你只需为默认实现添加一个扩展,它是Swift的一个基本方面.(正如下面的所有现代答案所示.)现在添加objc标志是错误的. (4认同)
  • 只是不要这样做!由于某种原因,Swift不支持协议中的可选方法. (3认同)
  • 此方法不支持参数中的选项.即你不能那样做`可选的func doSomething(param:Int?)` (2认同)
  • 因为“可选要求”(这本身就是一个矛盾修辞法)违背了契约+防止静态调度的思想。Swift 核心团队的一位成员(我不记得具体是谁)说 Swift 只有“可选协议要求”,因为 Obj-C 互操作需要它。此外,原生“可选需求”的想法在快速演进的早期很快就被抛弃了。 (2认同)

Ant*_*ine 393

在Swift 2及更高版本中,可以添加协议的默认实现.这为协议中的可选方法创建了一种新方法.

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

extension MyProtocol {
    func doSomethingOptionalMethod(){ 
        // leaving this empty 
    }
}
Run Code Online (Sandbox Code Playgroud)

它在创建可选协议方法方面不是一个非常好的方法,但是您可以在协议回调中使用结构.

我在这里写了一个小摘要:https: //www.avanderlee.com/swift-2-0/optional-protocol-methods/

  • @MattQuiros我发现你确实需要在协议定义中声明该函数,否则no-op扩展函数不会在符合协议的类中被覆盖. (12认同)
  • @FranklinYu你可以做到这一点,但是你正在用'throws'来规划你的API设计,而实际上并不需要它.我更喜欢"微协议"的想法.例如,每个方法确认一个协议,然后您可以检查:如果对象是协议 (4认同)
  • @IanPearce是正确的,这似乎是设计的.在WWDC的"面向协议的编程"讲座(408)中,他们谈到主协议中的方法是为符合类型提供的"定制点".必需的自定义点不会在扩展中收到定义; 一个可选的点.通常不应定制的协议的方法在扩展中完全声明/定义,以禁止符合类型自定义*除非您专门转换为其dynamicType*以显示您想要conformer的自定义实现. (3认同)
  • 这可能是在Swift中最干净的方法.太糟糕了,它在Swift 2.0之前不起作用. (2认同)
  • @Antoine我认为你应该在扩展公共中使用该函数,因为协议中的函数是公开的.当协议将在其模块之外使用时,您的解决方案将无法工作. (2认同)

小智 39

由于有一些关于如何使用可选修饰符@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*
Run Code Online (Sandbox Code Playgroud)

请注意,Objective-C代码无法调用协议扩展方法,更糟糕的是Swift团队无法修复它.https://bugs.swift.org/browse/SR-492


Rap*_*ael 33

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

设置您的协议:

@objc protocol MyProtocol:class
{
    func requiredMethod()
    optional func optionalMethod()
}

class MyClass: NSObject
{
    weak var delegate:MyProtocol?

    func callDelegate()
    {
        delegate?.requiredMethod()
        delegate?.optionalMethod?()
    }
}
Run Code Online (Sandbox Code Playgroud)

将委托设置为类并实现协议.请注意,不需要实现可选方法.

class AnotherClass: NSObject, MyProtocol
{
    init()
    {
        super.init()

        let myInstance = MyClass()
        myInstance.delegate = self
    }

    func requiredMethod()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

一个重要的是可选方法是可选的,需要一个"?" 在打电话时.提到第二个问号.

delegate?.optionalMethod?()
Run Code Online (Sandbox Code Playgroud)

  • 这应该是正确的答案.简单,清洁和解释. (2认同)

Zag*_*Zag 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"
Run Code Online (Sandbox Code Playgroud)

为了声明与swift一起使用的可选协议,将函数声明为变量而不是func.

protocol Health {
    var isInfoHealthy: (Info) -> (Bool)? { get set }
}
Run Code Online (Sandbox Code Playgroud)

然后按如下方式实施协议

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)?
}
Run Code Online (Sandbox Code Playgroud)

然后你可以用"?" 检查功能是否已实施

func returnEntity() -> Health {
    return Human()
}

var anEntity: Health = returnEntity()

var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))? 
//"isHealthy" is true
Run Code Online (Sandbox Code Playgroud)

  • 使用此解决方案,您无法从任何协议功能中访问self.在某些情况下,这可能会导致问题! (3认同)

Gau*_*iya 31

Swift 3.0中

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

它会节省您的时间.

  • 现在所有成员都需要"@ objc"的原因吗? (6认同)

Meh*_*mar 27

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

  • 这还要求您将实现协议的类标记为`@objc`,而不仅仅是协议. (5认同)

gue*_*nis 12

具有协议继承的纯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()
    }
}
Run Code Online (Sandbox Code Playgroud)


eLi*_*lie 11

为了说明Antoine答案的机制:

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"
Run Code Online (Sandbox Code Playgroud)


eme*_*mem 8

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

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

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


mat*_*mer 7

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

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

您可以在协议上创建属性可选

protocol SomeProtocol {
    var required: String { get }
    var optional: String? { get }
}
Run Code Online (Sandbox Code Playgroud)

在协议扩展中实现虚拟计算属性

extension SomeProtocol {
    var optional: String? { return nil }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用已实现或未实现可选属性的结构

struct ConformsWithoutOptional {
    let required: String
}

struct ConformsWithOptional {
    let required: String
    let optional: String?
}
Run Code Online (Sandbox Code Playgroud)

我还在我的博客上写了如何在Swift协议中可选属性,如果通过Swift 2版本发生变化,我会不断更新.


Him*_*shu 7

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

1 - 第一个选项是使用 @objc 属性标记您的协议。虽然这意味着它只能被类采用,但这确实意味着您将单个方法标记为可选,如下所示:

@objc protocol MyProtocol {
    @objc optional func optionalMethod()
}
Run Code Online (Sandbox Code Playgroud)

2 - 更快捷的方式:此选项更好。像这样编写什么都不做的可选方法的默认实现。

protocol MyProtocol {
    func optionalMethod()
    func notOptionalMethod()
}

extension MyProtocol {

    func optionalMethod() {
        //this is a empty implementation to allow this method to be optional
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 有一个叫做 extension 的特性,它允许我们为那些我们希望是可选的方法提供默认实现。


Sau*_*rma 5

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

@objc protocol InterViewDelegate:class {

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

}
Run Code Online (Sandbox Code Playgroud)