在Swift中可以吗?如果不是,那么是否有解决方法?


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类符合它。此外,这意味着您可以利用强大的泛型系统。 当遇到符合这种协议的类型时,您总是可以确保满足所有需求。它总是具体实现或默认实现。这就是“接口”或“契约”在其他语言中的表现。

缺点

For non-Void requirements, you need to have a reasonable default value, which is not always possible. However, when you encounter this problem, it means that either such requirement should really have no default implementation, or that your you made a mistake during API design. You can't distinguish between a default implementation and no implementation at all, at least without addressing that problem with special return values. Consider the following example: protocol SomeParserDelegate { func validate(value: Any) -> Bool } If you provide a default implementation which just returns true — it's fine at the first glance. Now, consider the following pseudo code: 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 */ } } } There's no way to implement such an optimization — you can't know if your delegate implements a method or not. Although there's a number of different ways to overcome this problem (using optional closures, different delegate objects for different operations to name a few), that example presents the problem clearly.


2. 使用@objc可选。

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

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

优势

不需要缺省实现。你只需要声明一个可选方法或变量就可以了。

缺点

它要求所有符合要求的类型都与Objective-C兼容,从而严重限制了协议的功能。这意味着,只有继承自NSObject的类才能符合这样的协议。没有结构,没有枚举,没有关联类型。 必须始终通过可选调用或检查符合类型是否实现了可选方法来检查是否实现了可选方法。如果经常调用可选方法,这可能会引入大量样板文件。


您需要在每个方法之前添加可选关键字。 但是,请注意,要使其工作,您的协议必须用@objc属性标记。 这进一步表明该协议将适用于类,但不适用于结构。


这里的其他答案涉及将协议标记为“@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

这里有一个非常简单的例子,只适用于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 2及以后的版本中,可以添加协议的默认实现。这为协议中的可选方法提供了一种新的方式。

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

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

这不是一个创建可选协议方法的好方法,但是给了你在协议回调中使用struct的可能性。

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


与最初的问题有点偏离主题,但它建立在安托万的想法上,我想它可能会帮助到一些人。

您还可以为具有协议扩展的结构设置可选的计算属性。

您可以将协议上的属性设置为可选的

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发布时情况发生变化。


下面是一个使用委托模式的具体示例。

设置您的协议:

@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?()

在Swift 3.0中

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

这会节省你的时间。


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


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


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

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

欲进一步阅读,请参阅https://useyourloaf.com/blog/swift-optional-protocol-methods/,该网站对此问题有很好的概述。


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

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"

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

要在swift中定义可选协议,你应该在协议声明和协议中的属性/方法声明之前使用@objc关键字。 下面是协议的可选属性示例。

@objc protocol Protocol {

  @objc optional var name:String?

}

//现在,如果尝试在代码中实现该协议,将不会强制在类中包含该函数。

class MyClass: Protocol {

   // No error

}

另一种方法是使用协议扩展,我们也可以给出该协议的默认实现。所以协议的功能是可选的。


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

@objc protocol InterViewDelegate:class {

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

}

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


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

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

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

有两种方法可以在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有一个叫做扩展的特性,它允许我们为那些我们想要成为可选的方法提供一个默认实现。


让我们先来了解一下它们的区别

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

第二个例子-如果你写UITableViewDelegate,并意识到它不会显示红色错误,请添加所有的委托方法。使用哪种方法取决于你自己。我们可以调用作为可选方法!

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

第一种Swift方式默认协议方法

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方法,所以这取决于你。这意味着With和不向类添加方法 它工作得很好。-在协议中作为可选方法调用 类声明类和协议 @objc属性,它只与类而不是struct工作!

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

注意:你可以选择Struct或Class

class UserModel{
    var firstname: String = ""
}

OR

struct UserModel{
    var firstname: String = ""
}

AND

protocol UserDataSource{
    func contactConfiguration(user: UserModel)
}

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

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