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

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

218243 次浏览

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运行时涉及(好吧,至少没有显式地)。这意味着你可以使struct, enum和非-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的类才能符合这样的协议。没有结构,没有枚举,没有关联类型。

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

  • 你需要在每个方法之前添加optional关键字。
  • 但是,请注意,要使其工作,您的协议必须用@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/ < / p >

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

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

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

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

斯威夫特3.0

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

这会节省你的时间。

如果你想在纯swift中实现,最好的方法是提供一个默认实现,特别是如果你返回一个swift类型,例如带有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()
}
}

那么你可以实现协议而不定义每个函数

由于有一些关于如何使用可选的修饰词@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中定义Optional Protocol,你应该在协议中的Protocol声明和attribute/method声明之前使用@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并且意识到它没有显示红色错误,请添加所有的委托方法。使用哪种方法取决于你自己。我们可以调用optional method!

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

第一种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工作!< / p >

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

注意:你可以选择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)
}
}