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

hnh*_*hnh 553 delegates swift swift-protocols

weak引用似乎在Swift中不起作用,除非a protocol声明为@objc,我不想在纯粹的Swift应用程序中.

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

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}
Run Code Online (Sandbox Code Playgroud)

我需要为协议添加前缀@objc,然后才能工作.

问题:什么是"纯粹的"Swift方式来实现weak delegate

fla*_*nez 1019

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

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

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}
Run Code Online (Sandbox Code Playgroud)

我的理解是使用class,你保证这个协议只用于类,而不是其他东西,如枚举或结构.

  • 我对这个解决方案的问题是调用委托导致崩溃 - EXC_BAD_ACCESS(如其他地方所述).这似乎是错误.我找到的唯一解决方案是使用@objc并从协议中删除所有Swift数据类型. (24认同)
  • 现在在Swift中做弱代表的正确方法是什么?Apple文档未在示例代码中显示或声明代表弱:https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/Protocols.html (11认同)
  • 顺便说一句:我认为“新样式”(Swift 5)是要执行“ protocol ProtocolNameDelegate:AnyObject”,但是没关系。 (4认同)
  • 这并不总是安全的-请记住,如果它还包含对委托人的引用,则只需要使该委托变弱,并且您需要中断该强引用周期。如果委托人没有对委托人的引用,则委托人可能会超出范围(因为它很弱),并且会导致崩溃和其他问题://需要牢记。 (2认同)

Sur*_*gch 272

补充答案

我一直很困惑代表们是否应该是弱者.最近我学到了更多关于代表以及何时使用弱引用的知识,所以让我在这里为了未来的观众添加一些补充点.

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

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

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

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

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

  • 作为一般规则,应将委托标记为,weak因为大多数委托都引用了他们不拥有的类.当孩子使用委托与父母沟通时,这肯定是正确的.但是,仍有一些情况下代表可以而且应该使用强引用.

  • 协议可用于引用类型(类)和值类型(结构,枚举).因此,在您需要使委托变弱的情况下,您必须将AnyObject关键字添加到协议中,以便它知道它仅用于引用类型.

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }
    
    Run Code Online (Sandbox Code Playgroud)

进一步研究

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

有关

  • 你是对的.我实际上和你之前有同样的问题,但我错过了很多这样的背景信息.我做了以上阅读并做了补充说明,以帮助自己理解与您的问题相关的所有问题.现在我想我可以应用你接受的答案,并知道我为什么这样做.我希望它也可以帮助未来的观众. (29认同)
  • 这一切都很有趣,但与我原来的问题并没有关系 - 这既不是关于弱/ ARC本身也不是关于为什么代表通常都很弱.我们已经知道所有这些,只是想知道*你如何声明一个弱协议引用(@flainez完美地回答). (5认同)
  • 但我可以有一个不依赖于类型的弱协议吗?其自身的协议并不关心什么对象符合自身.因此,类或结构都可以符合它.是否有可能仍然能够兼顾它,但只有符合要求的类类型? (5认同)

Tim*_*hen 36

AnyObject 是在Swift中使用弱引用的官方方法.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}
Run Code Online (Sandbox Code Playgroud)

来自Apple:

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

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

  • 有趣.在Swift 4.1中,"class"被弃用了吗? (7认同)

Wil*_*ust 9

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

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

  • 我认为值得注意的是\ @objc还有其他副作用 - 对于@eXhausted的NSObjectProtocol建议稍好一些.使用\ @objc - 如果类委托采用对象参数,如'handleResult(r:MySwiftResultClass)',则MySwiftResultClass现在需要从NSObject继承!并且可能它不再是名称空间,等等.简而言之:\ @objc是一个桥接功能,而不是语言功能. (4认同)