【问题标题】:How can I make a weak protocol reference in 'pure' Swift (without @objc)如何在“纯”Swift 中进行弱协议引用(没有@objc)
【发布时间】:2014-07-26 19:22:38
【问题描述】:

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

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

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

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

问题:完成@​​987654329@delegate 的“纯”Swift 方法是什么?

【问题讨论】:

标签: swift delegates swift-protocols


【解决方案1】:

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

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

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

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

【讨论】:

  • 我对这个解决方案的问题是调用委托会导致崩溃——EXC_BAD_ACCESS(正如其他地方的其他人所指出的那样)。这似乎是错误。我找到的唯一解决方案是使用 @objc 并从协议中消除所有 Swift 数据类型。
  • 现在在 Swift 中做弱委托的正确方法是什么? Apple 文档未在其示例代码中显示或声明委托为弱:developer.apple.com/library/ios/documentation/swift/conceptual/…
  • 这并不总是安全的 - 请记住,如果它还持有对委托人的引用,您只需将委托设为弱,并且您需要打破该强引用循环。如果委托没有对委托人的引用,则委托可能会超出范围(因为它很弱),并且您将遇到崩溃和其他问题:/ 请记住。
  • 顺便说一句:我认为“新风格”(Swift 5)是做protocol ProtocolNameDelegate: AnyObject,但没关系。
  • 它应该是AnyObject,因为class 将在某个时候被弃用。
【解决方案2】:

补充答案

我总是对代表是否应该弱感到困惑。最近我了解了更多关于委托以及何时使用弱引用的知识,所以我在此补充一些要点,以供将来的观众参考。

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

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

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

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

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

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

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

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

进一步研究

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

相关

【讨论】:

  • 这一切都很好,很有趣,但与我最初的问题并没有真正的关系——这既不是关于弱/ARC 本身,也不是关于为什么代表通常很弱。我们已经知道这一切,只是想知道如何你可以声明一个弱协议引用(@flainez 回答得很好)。
  • 你是对的。我实际上和你之前有同样的问题,但我错过了很多背景信息。我做了上述阅读并做了补充说明,以帮助自己理解与您的问题相关的所有问题。现在我想我可以应用您接受的答案并知道我为什么要这样做。我希望它也能对未来的观众有所帮助。
  • 但是我可以有一个不依赖于类型的弱协议吗?协议本身并不关心什么对象符合自己。所以类或结构都可以符合它。是否仍然有可能同时符合它的好处,但只有符合它的类类型是弱的?
  • > 因为大多数委托都在引用他们不拥有的类,所以我会将其重写为:大多数委托。否则非拥有对象成为所有者
【解决方案3】:

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

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

来自苹果:

为防止强引用循环,应将委托声明为 弱引用。有关弱引用的更多信息,请参阅 类实例之间的强引用循环。标记协议 因为仅类稍后将允许您声明委托必须 使用弱参考。您将协议标记为仅类 继承自 AnyObject,如 Class-Only Protocols 中所述。

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

【讨论】:

  • 有趣。 class 在 Swift 4.1 中被弃用了吗?
  • @hnh 您仍然可以通过将其设为类来创建“伪协议”,但协议:AnyObject 完全符合 OP 的要求,而且副作用比将其设为类要少。 (你仍然不能将这样的协议与值类型一起使用,但将其声明为类也不能解决这个问题)
【解决方案4】:

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

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

【讨论】:

  • 如前所述,这不是 IMO 问题的答案。一个普通的 Swift 程序应该能够独立于它自己而不受 NS'ism 的约束(这可能意味着不再使用委托,而是使用其他一些设计结构)。我的纯 Swift MyClass 实际上并不关心目标是结构还是对象,我也不需要可选项。也许他们稍后会修复它,毕竟这是一种新语言。如果需要引用语义,可能类似于“类协议 XYZ”?
  • 我认为还值得注意的是 \@objc 有额外的副作用 - @eXhausted 的 NSObjectProtocol 建议要好一些。使用 \@objc - 如果类委托接受对象参数,例如 'handleResult(r: MySwiftResultClass)',则 MySwiftResultClass 现在需要从 NSObject 继承!而且大概它也不再是命名空间了,等等。简而言之:\@objc 是一种桥接功能,而不是一种语言。
  • 我认为他们已经解决了这个问题。你现在写:protocol MyClassDelegate : class { }
  • 这方面的文档在哪里?要么是我失明,要么是做错了什么,因为我找不到任何有关这方面的信息……O_O
  • 我不确定它是否回答了 OP 的问题,但这很有帮助,尤其是当您与 Objc-C 进行互操作时;)
【解决方案5】:

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

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

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

【讨论】:

    【解决方案6】:

    protocol 必须是 AnyObject 的子类,类

    下面给出的例子

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

    【讨论】:

      【解决方案7】:

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

      public protocol UIScrollViewDelegate : NSObjectProtocol {
         ...
      }
      

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

      【讨论】:

      • 与问题无关,这个问题是关于构建一个支持委托对象的纯 Swift 类(特别是 no NSObject)。这与实现 Objective-C 协议无关,这就是您正在做的事情。后者需要@objc aka NSObjectProtocol。
      • 可以,但不推荐。
      猜你喜欢
      • 1970-01-01
      • 2016-08-17
      • 1970-01-01
      • 1970-01-01
      • 2015-11-18
      • 1970-01-01
      • 1970-01-01
      • 2018-09-04
      • 1970-01-01
      相关资源
      最近更新 更多