何时在Swift中使用`protocol`和`protocol: class`?

48

我已经设置了一个协议,用于将一些信息发送回上一个视图控制器。

我定义它如下:

protocol FilterViewControllerDelegate: class  {
    func didSearch(Parameters:[String: String]?)
}

但是使用时有什么区别呢:

protocol FilterViewControllerDelegate  {
        func didSearch(Parameters:[String: String]?)
    }

那什么时候我应该使用 : class 协议呢?


1
取决于符合 FilterViewControllerDelegate 的一切是不是一个 class,以及您是否希望将类型为 FilterViewControllerDelegate 的东西视为具有引用语义(例如,这将允许您使用 weak 属性,我怀疑您可能需要)。请参见以下示例:swift protocol 'weak' cannot be applied to non-class type - Hamish
2
请参见https://dev59.com/H4bca4cB1Zd3GeqPSzRB。 - rmaddy
5个回答

67

Swift 4版本

像这样在协议定义中添加AnyObject

protocol FilterViewControllerDelegate: AnyObject  {
    func didSearch(parameters:[String: String]?)
}
只有一个类能够符合该协议的要求。
所以,考虑到这一点。
protocol FilterViewControllerDelegate: AnyObject  {
    func didSearch(parameters:[String: String]?)
}

你将能够编写这个

class Foo: FilterViewControllerDelegate {
    func didSearch(parameters:[String: String]?) { }
}

但是不是这个

struct Foo: FilterViewControllerDelegate {
    func didSearch(parameters:[String: String]?) { }
}

Swift 3 版本

像这样的协议定义中添加 :class 关键字

protocol FilterViewControllerDelegate: class  {
    func didSearch(Parameters:[String: String]?)
}

这意味着只有一个类能够符合该协议。

那么,考虑到这一点

protocol FilterViewControllerDelegate: class  {
    func didSearch(Parameters:[String: String]?)
}

您将能够编写此内容

class Foo: FilterViewControllerDelegate {
    func didSearch(Parameters:[String: String]?) { }
}

但是不是这个

struct Foo: FilterViewControllerDelegate {
    func didSearch(Parameters:[String: String]?) { }
}

1
Swift 4文档建议在协议声明后使用AnyObject而不是class。有什么区别? - nekonari
1
正如@nekonari所说,关于Swift 4语法,您应该使用AnyObject。请查看这个优秀的视频https://www.youtube.com/watch?v=h5c3MDbgn-c&t=966s - swift2geek
如果我们不使用“AnyObject”,是否意味着所有三个类、结构体和枚举都可以符合该协议? - subin272

29

在使用class/AnyObject关键字标记协议时,还有一件事情需要注意。

例如给定这样一个协议:



Swift 4及以上版本(根据文档):

protocol FilterViewControllerDelegate: AnyObject  {
    func didSearch(with parameters: [String: String]?)
}

Swift 4之前的语法:

protocol FilterViewControllerDelegate: class  {
    func didSearch(with parameters: [String: String]?)
}


例如,假设您正在创建一个具有代理属性为FilterViewControllerDelegate类型的DetailViewController:

class DetailViewController: UIViewController {
    weak var delegate: FilterViewControllerDelegate
}

如果您没有使用 class 关键字标记该协议,您将无法将该 delegate 属性标记为 weak 属性。

为什么呢?

很简单 - 只有基于类的属性才能具有弱引用关系。如果您试图避免引用循环,那就是正确的方法。


3
到目前为止,这是我读过的最好的答案。每个人都在说它的意义,但没有人说为什么应该使用它。谢谢! - Mostafa
很棒的总结。这比我在这个主题上读过的任何其他东西都更有帮助。 - Alex Zavatone
谢谢,但是弱化委托属性有什么好处呢?我的意思是,在现实生活中是否存在任何条件,如果我们不弱化委托属性就可能发生内存泄漏? - Mumtaz Hussain

11

Swift 5.1, Xcode 11 语法:

protocol FilterViewControllerDelegate: AnyObject {
       func didSearch(Parameters:[String: String]?)
}

该协议仅适用于类。

回答你的第一个问题 -

但是使用时有什么区别:

与此相比的不同之处:

protocol FilterViewControllerDelegate  {
        func didSearch(Parameters:[String: String]?)
}

需要注意的是,该协议可以采用值类型,如枚举和结构体等。

回答你的第二个问题 -

什么时候应该使用:class协议?

当您需要使用类协议时,我想通过委托模式中的下一个示例来进行描述:

假设您有委托协议。

protocol PopupDelegate: AnyObject {
    func popupValueSelected(value: String)
}

并且在另一个类中,你想要创建一个属性

var delegate: PopupDelegate?

但是这个有强引用,可能会导致内存泄漏问题。修复内存泄漏的一种方式是将委托属性设为弱引用。在我们只允许协议应用于类之前,Swift 会认为我们还可以将协议应用于值类型。

weak var delegate: PopupDelegate?

如果您尝试像使用weak一样声明委托,您将看到下一个错误:

'weak' var只能应用于类和绑定类协议类型,而不是'PopupDelegate'

但是,我们不能将weak应用于值类型。 因此,我们需要将我们的协议限制为引用类型,以便Swift知道它是引用类型。要使您可以将此委托声明为weak,您需要将该协议限制为仅由类使用:

protocol PopupDelegate: AnyObject {
    func popupValueSelected(value: String)
}

6
这意味着你定义的协议只能被类所采用,而不能被结构体或枚举所采用。
引自官方Swift书籍:Protocols
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // class-only protocol definition goes here } 

在上面的例子中,SomeClassOnlyProtocol 只能被类类型采用。如果尝试编写一个结构体或枚举定义来采用 SomeClassOnlyProtocol,则会在编译时出现错误。

我发现这段代码使用了一个类协议: protocol MenuViewControllerDelegate: class { func menuViewControllerDidTouchTop(controller: MenuViewController) } 但是在协议函数中将VC作为参数发送的目的是什么? - user2636197
它的目的是您可以从该参数中获取一些有用的数据。例如,您可以在控制器中设置一些变量,然后通知您的委托关于事件(例如用户保存操作)。将vc传递给其委托使得可以从调用者中提取任何公共数据。 - alexburtnik
它不会获取任何数据。在VC1中,它所做的只有这个: func menuViewControllerDidTouchTop() { animateMenuButton() } 它运行一个触发动画的函数。所以我不明白为什么它需要任何数据。 - user2636197
有时候委托并不需要知道除了事件发生之外的任何信息,但是在某些情况下,它确实需要使用调用者的一些数据,就像我所说的那样。因此,定义一个协议来满足这些需求是一个好的做法,然后委托可以忽略它或使用它。 - alexburtnik
主要思想是可能有多个对象实现您定义的协议。其中一些对象可能会忽略您传递的参数,而另一些对象可能会使用它。 - alexburtnik

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接