如何在“纯”Swift (没有@objecc)中创建弱协议引用

除非 protocol被声明为 @objc,否则 weak引用在 Swift 中似乎不起作用,我不希望在一个纯 Swift 应用程序中使用 @objc

这段代码提供了一个编译错误(weak不能应用于非类类型 MyClassDelegate) :

class MyClass {
weak var delegate: MyClassDelegate?
}


protocol MyClassDelegate {
}

我需要给协议加上 @objc的前缀,然后它就可以工作了。

问: 完成 weak delegate的“纯”Swift 方法是什么?

131967 次浏览

< >强更新: 看起来手册已经更新了,我提到的例子已经被删除了。请看上面对@flainez的回答的编辑

< >强原: 使用@objc是正确的方法,即使你没有与Obj-C进行互操作。它确保您的协议应用于类,而不是枚举或结构。

.请参见手册中的“Checking for Protocol Conformance”

您需要声明协议的类型为AnyObject

protocol ProtocolNameDelegate: AnyObject {
// Protocol stuff goes here
}


class SomeClass {
weak var delegate: ProtocolNameDelegate?
}

使用AnyObject可以说明只有类可以符合此协议,而结构或枚举则不能。

补充回答

我总是困惑于委托是否应该是弱的。最近,我了解了更多关于委托和何时使用弱引用的知识,因此,为了将来的观众,让我在这里补充一些要点。

  • 使用weak关键字的目的是避免强引用循环(保留循环)。当两个类实例彼此具有强引用时,就会发生强引用循环。它们的引用计数永远不会归零,所以它们永远不会被释放。

  • 如果委托是一个类,则只需要使用weak。Swift结构体和枚举是值类型(它们的值在创建新实例时被复制),而不是引用类型,因此它们不会形成强reference循环。

  • weak引用总是可选的(否则你会使用unowned),总是使用var(而不是let),这样当它被释放时,可选的可以被设置为nil

  • 父类自然应该具有对其子类的强引用,因此不使用weak关键字。但是,当子进程需要对父进程的引用时,应该使用weak关键字将其设置为弱引用。

  • 当你想引用一个你不拥有的类时,应该使用weak,而不仅仅是用于子类引用它的父类。当两个非层次类需要相互引用时,选择一个为弱类。你选择哪种取决于具体情况。参见这个问题的答案。

  • 作为一般规则,委托应该被标记为weak,因为大多数委托引用的是它们不拥有的类。当子进程使用委托与父进程通信时,这是绝对正确的。文档建议对委托使用弱引用。(但是参见this。)

  • 协议可以用于引用类型(类)和值类型(结构,枚举)。所以在可能的情况下,你需要让一个委托弱,你必须让它成为一个对象协议。实现方法是将AnyObject添加到协议的继承列表中。(在过去,你使用class关键字,但AnyObject现在是首选。)

    protocol MyClassDelegate: AnyObject {
    // ...
    }
    
    
    class SomeClass {
    weak var delegate: MyClassDelegate?
    }
    

Further Study

Reading the following articles is what helped me to understand this much better. They also discuss related issues like the unowned keyword and the strong reference cycles that happen with closures.

Related

苹果使用“NSObjectProtocol”而不是“class”。

public protocol UIScrollViewDelegate : NSObjectProtocol {
...
}

这也适用于我,并删除了我在尝试实现自己的委托模式时所看到的错误。

AnyObject是Swift中使用弱引用的官方方式。

class MyClass {
weak var delegate: MyClassDelegate?
}


protocol MyClassDelegate: AnyObject {
}

从苹果公司:

为了防止强引用循环,委托应该声明为 弱引用。有关弱引用的详细信息,请参见 类实例之间的强引用循环。标记协议 As -only稍后将允许您声明委托必须 使用弱引用。将协议标记为仅类

.从AnyObject继承,如仅类协议中所述

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID276

protocol必须是AnyObject的子类,class

示例如下

    protocol NameOfProtocol: class {
// member of protocol
}
class ClassName: UIViewController {
weak var delegate: NameOfProtocol?
}

弱限定符只适用于参考对象。除非在协议上添加@objcAnyObjectclass限定符,否则符合协议的对象可能不是引用对象。

因此,您需要这些限定符中的一个(建议使用AnyObject,因为class预计将被弃用)。

顺便说一下,请注意,在你的类和属性中添加@objc有时是必要的,即使在“纯swift”中也是如此。应用程序。这与你的开发语言无关。它导致编译器以一种与Objective-C运行时兼容的方式构建代码,这对于某些操作系统接口(例如目标/动作和旧式键路径)是必需的。