590

weak引用似乎在 Swift 中不起作用,除非 aprotocol被声明为@objc,这是我在纯 Swift 应用程序中不想要的。

此代码给出编译错误(weak不能应用于非类类型MyClassDelegate):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

我需要在协议前面加上@objc,然后它才能工作。

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

4

7 回答 7

1108

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

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

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

使用AnyObject你说只有类可以符合这个协议,而结构或枚举不能。

于 2014-06-08T08:35:30.417 回答
316

补充答案

我总是对代表是否应该弱感到困惑。最近我学到了更多关于委托以及何时使用弱引用的知识,所以为了以后的观众,让我在这里添加一些补充点。

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

  • weak仅当委托是类时才需要使用。Swift 结构和枚举是值类型(它们的值在创建新实例时被复制),而不是引用类型,因此它们不会产生强引用循环。

  • weak引用始终是可选的(否则您将使用unowned)并且始终使用var(not let),以便在nil释放它时可以将可选设置为。

  • 父类自然应该对其子类具有强引用,因此不应使用weak关键字。但是,当子级想要引用其父级时,应该使用weak关键字使其成为弱引用。

  • weak当您想要引用不属于您的类时,应该使用它,而不仅仅是引用其父类的子类。当两个非层次类需要相互引用时,选择一个弱。您选择哪种取决于具体情况。有关更多信息,请参阅此问题的答案。

  • 作为一般规则,应将委托标记为,weak因为大多数委托都引用了他们不拥有的类。当孩子使用委托与父母沟通时,这绝对是正确的。文档建议对委托使用弱引用。(但也看到这个。)

  • 协议可用于引用类型(类)和值类型(结构、枚举)。因此,在您需要弱化委托的可能情况下,您必须使其成为仅对象协议。这样做的方法是添加AnyObject到协议的继承列表中。(过去您使用class关键字执行此操作,但AnyObject现在首选。)

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

进一步研究

阅读以下文章有助于我更好地理解这一点。他们还讨论了相关问题,例如unowned关键字和闭包所发生的强引用循环。

有关的

于 2016-01-02T14:20:16.193 回答
40

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

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

来自苹果:

为了防止强引用循环,应将委托声明为弱引用。有关弱引用的更多信息,请参阅类实例之间的强引用循环。将协议标记为仅类将允许您声明委托必须使用弱引用。您可以通过继承AnyObject将协议标记为仅类,如仅类协议中所述。

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

于 2018-02-19T14:18:05.453 回答
8

更新: 看起来手册已更新,我所指的示例已被删除。请参阅上面对@flainez 答案的编辑。

原文: 即使您没有与 Obj-C 互操作,使用 @objc 也是正确的方法。它确保您的协议应用于类而不是枚举或结构。请参阅手册中的“检查协议一致性”。

于 2014-06-07T06:26:45.970 回答
1

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

因此,您需要这些限定符中的一个(并且AnyObject被推荐,因为class预计会被弃用。)

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

于 2021-06-17T11:57:16.290 回答
-1

协议必须是 AnyObject 的子类,类

下面给出的例子

    protocol NameOfProtocol: class {
   // member of protocol
    }
   class ClassName: UIViewController {
      weak var delegate: NameOfProtocol? 
    }
于 2019-04-30T13:02:45.047 回答
-9

Apple 使用“NSObjectProtocol”而不是“类”。

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

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

于 2016-04-19T16:49:43.627 回答