【问题标题】:When to use `protocol` and `protocol: class` in Swift?何时在 Swift 中使用 `protocol` 和 `protocol: class`?
【发布时间】:2020-02-25 02:27:34
【问题描述】:

我已经设置了一个协议来将一些信息发送回之前的 VC。

我是这样定义的:

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

但是使用时有什么区别:

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

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

【问题讨论】:

标签: swift swift-protocols


【解决方案1】:

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]?) { }
}

【讨论】:

  • Swift 4 文档说在协议声明后使用AnyObject 而不是class。有什么区别?
  • 正如@nekonari 所说,关于 Swift 4 语法,您应该使用 AnyObject。请看这个优秀的视频youtube.com/watch?v=h5c3MDbgn-c&t=966s
  • @LucaAngeletti 如果我们不使用“AnyObject”是否意味着所有三个 Class、Struct 和 enum 都可以符合该协议?
【解决方案2】:

使用class/AnyObject 关键字标记协议还有另一件事。

给定这样的协议:

Swift 4 及以上(根据docs):

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】:

Swift 5.1,Xcode 11 语法:

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

这个协议只能被类采用。

回答你的第一个问题 -

但是使用时有什么区别:

与此的区别:

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

是这个协议可以采用值类型,例如枚举和结构。

回答你的第二个问题 -

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

当你应该使用类协议时,我想描述下一个委托模式的例子: 想象一下,您有委托协议。

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

在另一个类中你想创建一个属性

var delegate: PopupDelegate?

但这有很强的参考性,可能会给您带来内存泄漏问题。修复内存泄漏的一种方法是使委托属性变弱。除非我们不会让我们的协议仅适用于类,否则 Swift 认为我们也可以将我们的协议应用于值类型。

weak var delegate: PopupDelegate?

如果您尝试将您的委托声明为弱,您将看到下一个错误:

'weak' var 仅适用于类和类绑定协议类型, 不是'PopupDelegate'

但是我们不能对值类型应用弱。所以我们需要将我们的协议限制为一个引用类型,所以 swift 知道它是一个引用类型。 为了让您可以将此委托声明为弱,您需要将您的协议限制为仅由类使用:

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

【讨论】:

    【解决方案4】:

    这意味着你定义的协议只能被类采用,不能被结构或枚举所采用。

    来自Official Swift book

    protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
        // class-only protocol definition goes here } 
    

    在上面的例子中,SomeClassOnlyProtocol 只能被类类型采用。这是 编写结构或枚举定义的编译时错误 尝试采用 SomeClassOnlyProtocol。

    【讨论】:

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

    Swift 3.2 更新:

    现在要声明仅类协议:

    protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
        // class-only protocol definition goes here
    }
    

    而不是

    protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
        // class-only protocol definition goes here
    }
    

    第二个 sn-p 现在似乎仍然有效。 参考:https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-15
      • 1970-01-01
      相关资源
      最近更新 更多