如何在“纯” Swift中创建弱协议引用(不带@objc)


561

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

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

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

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

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


Answers:


1038

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

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

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

AnyObject你的话说,只有类才能符合此协议,而结构或枚举则不能。


25
这个解决方案的我的问题是,调用委托会导致崩溃-EXC_BAD_ACCESS(如其他地方的人所指出的)。这似乎是错误。我发现的唯一解决方案是使用@objc并从协议中消除所有Swift数据类型。
Jim T

12
现在在Swift中做弱代表的正确方法是什么?苹果文件没有显示或声明的委托作为弱在他们的示例代码:developer.apple.com/library/ios/documentation/swift/conceptual/...
C0D3

2
这并不总是安全的-请记住,如果它还包含对委托人的引用,则只需要使该委托变弱,并且您需要中断该强引用周期。如果委托人没有对委托人的引用,委托人可能会超出范围(因为它很弱),并且会导致崩溃和其他问题://需要牢记。
Trev14

5
顺便说一句:我认为“新样式”(Swift 5)是可以做的protocol ProtocolNameDelegate: AnyObject,但没关系。
hnh

1
应该是AnyObject因为class它将在某个时候被弃用。
乔斯(José)

283

补充答案

我一直对代表是否应该软弱感到困惑。最近,我了解了有关委托的详细信息以及何时使用弱引用,因此为了将来的读者,让我在此处添加一些补充要点。

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

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

  • weak引用始终是可选的(否则,您将使用unowned),并且始终使用var(非let),以便可以在nil取消分配可选项时将其设置为。

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

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

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

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

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

进一步研究

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

有关


5
这一切都很好,也很有趣,但是与我最初的问题并没有真正的关系,既不是关于弱点/ ARC本身,也不是关于为什么通常代表弱点的原因。我们已经知道了所有这些,并且想知道如何声明一个弱协议引用(@flainez回答得很好)。
hnh

30
你是对的。实际上,我和您之前有相同的问题,但是我缺少很多此类背景信息。我做了上述阅读,并做了补充说明,以帮助自己理解与您的问题有关的所有问题。现在,我想我可以应用您接受的答案,并且知道我为什么这样做。我希望这对将来的观众也有帮助。
Suragch '16

5
但是我可以使用不依赖于类型的弱协议吗?协议本身并不关心哪个对象符合自身。因此,一个类或一个结构都可以符合它。是否仍然可以同时拥有两者的优势,但只有一致的类类型很弱?
FlowUI。SimpleUITesting.com

>因为大多数委托都引用了他们不拥有的类,所以我将其重写为:大多数委托。否则,非拥有的对象将成为所有者
Victor Jalencas

36

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


7
有趣。是class在雨燕4.1弃用?
hnh '18

@hnh仍然可以通过将其设为一个类来创建“伪协议”,但是协议:AnyObject可以完全按照OP的要求进行操作,而其副作用却小于使其成为一个类。(您仍然不能将这样的协议与值类型一起使用,但是将其声明为类也不会解决该问题)
Arru

8

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

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


如前所述,这不是IMO对问题的答案。一个普通的Swift程序应该能够独立于NS'ism而独立运行(这可能意味着不再使用委托的程序,而是使用其他一些设计构造)。我纯粹的Swift MyClass实际上并不关心目标是结构还是对象,也不需要可选项。也许他们以后可以修复它,毕竟这是一种新语言。如果需要引用语义,可能类似于“类协议XYZ”吗?
hnh 2014年

4
我认为\ @objc还具有其他副作用-@eXhausted的NSObjectProtocol建议要好一些。使用\ @objc-如果类委托接受对象参数,例如'handleResult(r:MySwiftResultClass)',则MySwiftResultClass现在需要从NSObject继承!大概也不再是命名空间了,等等。总之:\ @objc是一种桥接功能,而不是一种语言功能。
hnh 2014年

我认为他们已经解决了这个问题。您现在编写:protocol MyClassDelegate:类{}
user3675131

文件在哪里?我是瞎子还是做错了什么,因为我找不到关于此的任何信息... O_O
BastiBen 2014年

我不确定它是否回答了OP的问题,但这很有用,特别是如果您与Objc-C进行互操作;)
Dan Rosenstark

-1

协议必须是AnyObject的子类,类

下面给出的例子

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

-9

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

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

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


5
与该问题无关,该问题与构建支持委托对象的纯Swift类(特别是没有 NSObject)有关。这与实现Objective-C协议无关。后者需要@objc aka NSObjectProtocol。
hnh '16

可以,但不建议使用。
DawnSong
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.