【问题标题】:Delegate not getting set代表未设置
【发布时间】:2014-07-18 12:12:55
【问题描述】:

我正在用 Swift 构建一个框架,它使用用 Objective-C 编写的 GCDAsyncSocket。

我收到的错误是:

Couldn't start socket: Error Domain=GCDAsyncSocketErrorDomain Code=1 "Attempting to accept without a delegate. Set a delegate first." UserInfo=0x61000026b940 {NSLocalizedDescription=Attempting to accept without a delegate. Set a delegate first.}

我已经尝试在 init 方法中设置委托(如下所示),还尝试在初始化后使用 setDelegate 方法进行设置。

在调试时,我已经验证 setDelegate 代码被调用并且传入的值 (self) 实际上包含一个引用。

更新:当我修改 GCDAsyncSocket.m 以从委托的声明中删除 __weak 关键字时,它可以工作,但我仍然不明白为什么我必须这样做。 该行是:__weak id delegate,改为id delegate

这是导致问题的类:

class Server: GCDAsyncSocketDelegate
{
    let boundHost:String?
    let port:UInt16
    var socket:GCDAsyncSocket?

    init(boundHost: String?, port: UInt16)
    {
        if boundHost {
            self.boundHost = boundHost!
        }
        self.port = port

        println("Server created with host: \(self.boundHost) and port: \(self.port).")
    }

    convenience init(port:UInt16) {
        self.init(boundHost: nil, port: port)
    }

    func startServer() -> Bool
    {
        if !socket
        {
            socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
        }

        var error:NSError?
        if !socket!.acceptOnInterface(boundHost, port: port, error: &error)
        {
            println("Couldn't start socket: \(error)")
            return false;
        }
        else
        {
            println("Listening on \(port).")
            return true
        }
    }

    func stopServer() -> Bool
    {
        if socket
        {
            socket!.disconnect()
            return true
        }
        return false
    }

    func socket(sock:GCDAsyncSocket!, didAcceptNewSocket newSocket:GCDAsyncSocket!)
    {
        println("New socket received: \(newSocket)")
    }
}

【问题讨论】:

  • 可能是因为没有任何东西保留代理,所以 ARC 从内存中释放它。通过删除__weak 语句,GCDAsyncSocket 保留了委托,并且它不会被 ARC 释放。
  • 这听起来像是正在发生的事情,但它应该由 Server.swift 保留在 var socket 中。
  • 我对GCDAsyncSocket不熟悉,但是去看了class的header,delegate属性被声明为weak,所以没有保留。
  • 您打算将您的框架开源还是在线发布到任何地方?
  • @nsij22 它不完整,但有它。 github.com/picciano/SwiftServe

标签: ios objective-c macos delegates swift


【解决方案1】:

这是 Swift 代码中的 GCDAsyncSocket 和委托的问题(也可以应用于其他委托场景)。

你在这里遇到的是:

  1. Swift 为调用 GCDAsyncSocket() 创建对 self 的引用

  2. Objective-C 对其进行了处理,但将其视为 __weak 基本上不会创建 Swift 需要关心的额外引用。

  3. Swift 完成对 GCDAsyncSocket() 的调用,并且 ARC 删除了对 self 的引用,因为没有其他引用。 Swift 不知道 Objective-C 仍然需要它。

  4. 由于委托被声明为 __weak,它在 Objective-C 部分中也消失了。

  5. GCDAsyncSocket 不再有委托并抱怨它。

解决方案:

  1. 将 NSObject 作为超类添加到您的 Swift 类中,然后将类中的“self”保留到 init() 中的某个全局类变量中。然后 ARC 看到参考,一切都很好。这将在 Swift 端为 Object 的生命周期创建一个引用,并且 Objective-C 中的 __weak 将按预期工作。

  2. 或者从 GCDAsyncSocket 中删除 __weak 并在 Swift 类的 Deinit() 方法中添加一个 socket.Delegate(nil)。这将在 Objective-C 端创建一个引用,防止 Swift 端的 ARC 释放它,直到您手动执行。

GCDAsyncSocket 尽量不泄漏内存。在纯 Objective-C 项目中,这可以正常工作,但是在 Swift 中使用 Objective-C 时,如果委托在 Objective-C 中声明为 __weak,则需要在 Swift 端保留委托。

【讨论】:

  • 我将答案标记为正确,因为删除 __weak 确实解决了问题。我仍然不明白的部分是 Server 的实例及其 var socket 没有被取消初始化并且仍然处于活动状态。只是套接字没有得到它的委托集。即使是逐行调试也会显示 setDeletage 正在发生,但从不工作并且不会产生错误。
  • var socket 是您调用 GCCDAsyncSocket 的结果。但是 self() 是一个参数。 GCDAsyncSocket 的结果被保留,但在调用期间使用的参数不被保留,因为在调用之前或期间它们没有被任何东西引用。在 Swift 中,每个参数都是一个引用,没有任何东西是按值传递的,这就是问题的根本原因。如果没有强引用,一旦调用的方法完成, self() 的结果就会消失。在纯 Objective-C 中,它的工作原理是 self 通过值而不是引用传递。
  • 使用 Xcode 6.1.1,我能够通过将 Swift 类设为 NSObject 的子类来解决此问题(无需进行任何保留)。
猜你喜欢
  • 2014-08-16
  • 1970-01-01
  • 2018-07-03
  • 1970-01-01
  • 2019-04-18
  • 1970-01-01
  • 2012-02-17
  • 1970-01-01
  • 2020-11-27
相关资源
最近更新 更多