409

斯威夫特有可能吗?如果没有,那么有解决方法吗?

4

20 回答 20

610

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才能符合这样的协议。没有结构,没有枚举,没有关联的类型。

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

于 2014-06-04T08:52:09.893 回答
412

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

于 2015-06-09T23:19:40.977 回答
42

这是委托模式的具体示例。

设置您的协议:

@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?()
于 2015-10-20T08:46:47.200 回答
40

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

于 2017-04-28T12:01:44.477 回答
35

此处涉及将协议标记为“@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
于 2014-08-11T23:47:46.147 回答
34

Swift 3.0中

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

这将节省您的时间。

于 2016-12-16T14:22:15.140 回答
28
  • 您需要optional在每个方法之前添加关键字。
  • 但是请注意,要使其正常工作,您的协议必须标有 @objc 属性。
  • 这进一步意味着该协议将适用于类但不适用于结构。
于 2014-06-04T08:56:27.357 回答
14

具有协议继承的纯 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()
    }
}
于 2018-04-21T12:19:56.217 回答
11

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

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"
于 2017-09-30T00:21:34.563 回答
11

有两种方法可以在 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 有一个称为扩展的功能,它允许我们为那些我们希望成为可选的方法提供默认实现。

于 2020-04-07T06:27:02.657 回答
8

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

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

如需进一步阅读,请参阅https://useyourloaf.com/blog/swift-optional-protocol-methods/,它对此事提供了很好的概述。

于 2017-06-28T13:36:49.900 回答
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?
}

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

于 2015-08-29T06:32:00.303 回答
5

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

@objc protocol InterViewDelegate:class {

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

}
于 2019-02-14T08:03:58.860 回答
3

这是一个非常简单的示例,仅适用于快速类,不适用于结构或枚举。请注意,协议方法是可选的,有两个级别的可选链在起作用。采用该协议的类也需要在其声明中添加@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!)
    }

 }
于 2015-03-05T00:49:12.490 回答
3

如果您想以纯 Swift 的方式执行此操作,最好的方法是提供一个默认的实现细节,如果您返回一个 Swift 类型,例如 带有 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()
    }
}

那么你可以在不定义每个函数的情况下实现协议

于 2017-02-22T14:29:40.863 回答
0

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

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

let squareCalculator =
    MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
于 2019-09-26T22:49:00.847 回答
0

先来了解一下区别

第一个例子 -如果你写UITableViewDataSource,那么你需要强制编写两个方法 - 这是默认协议的快捷方式

第二个示例 -如果您编写UITableViewDelegate并意识到它没有显示红色错误,请添加所有委托方法。这取决于您要使用哪种方法。我们可以称为optional method

让我们通过一个例子来理解这一点

First Swift Way 默认协议方法

class ContactModel{
    var firstname: String?
    var lastname: String?
}

protocol ContactDataSource: AnyObject{
    func contactConfiguration(contact: ContactModel)
}

class ViewController: ContactDataSource{
    func contactConfiguration(contact: ContactModel) {
        print(contact)
    }
}

第二种方法 - 任择议定书

@objc
class UserModel: NSObject{
    var firstname: String = ""
}

@objc protocol UserDataSource{
    func contactConfiguration(user: UserModel)
   @objc optional func userInfo(user: UserModel)
}

class ViewController: UserDataSource{
    func contactConfiguration(user: UserModel) {
        print(user)
    }
}

注意:如果您可以在可选协议中看到,我还没有编写 userInfo 方法,所以由您决定。这意味着无论是否向类添加方法都可以正常工作。- 作为协议中的可选方法调用

您必须使用 @objc 属性声明类和协议,并且它仅适用于 Class 而不是 struct!

第三种方法 - 使用扩展的可选协议

注意:你可以选择 Struct 或 Class

class UserModel{
    var firstname: String = ""
}

或者

struct UserModel{
    var firstname: String = ""
}

protocol UserDataSource{
    func contactConfiguration(user: UserModel)
}

extension UserDataSource{
    func userInfo(user: UserModel){}
}

class myview: UserDataSource{
    func contactConfiguration(user: UserModel) {
        print(user)
    }
}
于 2021-12-25T07:35:24.793 回答
-1

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

于 2019-09-11T16:17:05.820 回答
-2

Optional Protocol要在 swift 中定义,您应该在该协议内的声明和/声明@objc之前使用关键字。下面是一个协议的可选属性示例。Protocolattributemethod

@objc protocol Protocol {

  @objc optional var name:String?

}

class MyClass: Protocol {

   // No error

}
于 2018-08-03T11:31:12.870 回答
-24

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

于 2014-06-04T08:47:03.690 回答