丢失对 Swift 中实例的 属性 的引用

Losing reference to a property of an instance in Swift

我遇到一个问题,我创建的 class 实例的 属性 似乎失去了对其值的引用。

基本上我有一个 class 这样的:

class Channel {    
    var callbacks: [String: (JSON) -> Void]
    var subscribed = false
    let name: String

    init(name: String) {
        self.name = name
        self.callbacks = [:]
    }

    func bind(eventName: String, callback: (JSON) -> Void) {
        self.callbacks[eventName] = callback
    }

    func handleEvent(eventName: String, eventData: String) {
        if let cb = self.callbacks[eventName] {
            let json = JSON(object: eventData)
            cb(json)
        }
    }
}

然后在 ViewController 中,我有以下代码:

class ViewController: UIViewController {
    let wSock = wSocket(key: "afa4d38348f89ba9c398")

    func channelSetup() {
        var ch = wSock.subscribe("test-channel")
        ch.bind("test-event", { (data: JSON) -> Void in
            println("I'm the callback getting called")
        })
        println(ch.callbacks)
    }

    override func viewDidLoad() {
        super.viewDidLoad()        

        channelSetup()        
    }
}

ch.callbacks的println中显示字典中存在键值对

但是,当通过套接字接收到消息时通道稍后接收到事件时,回调将不再存在。在代码方面,这里是完整的代码:

import UIKit

class ViewController: UIViewController {
    let wSock = wSocketClient(key: "afa4d38348f89ba9c398")

    func channelSetup() {
        var ch = wSock.subscribe("test-channel")
        ch.bind("test-event", { (data: JSON) -> Void in
            println("I'm the callback getting called")
        })
        println(ch.callbacks)
    }

    override func viewDidLoad() {
        super.viewDidLoad()        

        channelSetup()        
    }
}

class wSocketClient {
    let connection: Connection

    init(key: String, encrypted: Bool = false) {
        var url = "SOCKET_URL"
        connection = Connection(url: url)
    }

    func subscribe(channelName: String) -> Channel {
        return self.connection.addChannel(channelName)
    }

    func connect() {
        self.connection.open()
    }
}

class Connection: WebSocketDelegate {
    let url: String
    lazy var socket: WebSocket = { [unowned self] in
        return self.connectInternal()
    }()
    let connected = false
    var channels = Channels()

    init(url: String) {
        self.url = url
    }

    func addChannel(channelName: String) -> Channel {
        return Channel(name: channelName)
    }

    func open() {
        if self.connected {
            return
        } else {
            self.socket = connectInternal()
        }
    }

    func connectInternal() -> WebSocket {
        let ws = WebSocket(url: NSURL(string: self.url)!)
        ws.delegate = self
        ws.connect()
        return ws
    }

    func websocketDidReceiveMessage(text: String) {
        let data = (text as NSString).dataUsingEncoding(NSUTF8StringEncoding)
        let json = JSON(data: data!)

        if let channelName = json["channel"].stringValue {
            if let internalChannel = self.channels.find(channelName) {
                if let eName = json["event"].stringValue {
                    if let eData = json["data"].stringValue {
                        internalChannel.handleEvent(eName, eventData: eData) // this is the part of the code where the channel should eventually call the callback
                    }
                }
            }
        }
    }
}

class Channel {    
    var callbacks: [String: (JSON) -> Void]
    var subscribed = false
    let name: String

    init(name: String) {
        self.name = name
        self.callbacks = [:]
    }

    func bind(eventName: String, callback: (JSON) -> Void) {
        self.callbacks[eventName] = callback
    }

    func handleEvent(eventName: String, eventData: String) {
        if let cb = self.callbacks[eventName] { // here self.callbacks is empty and the callback has disappeared
            let json = JSON(object: eventData)
            cb(json)
        }
    }
}

class Channels {
    var channels = [String: Channel]()

    func add(channelName: String) -> Channel {
        if let channel = self.channels[channelName] {
            return channel
        } else {
            let newChannel = Channel(name: channelName)
            self.channels[channelName] = newChannel
            return newChannel
        }
    }

    func find(channelName: String) -> Channel? {
        return self.channels[channelName]
    }
}

所以基本上,当 WebSocket 接收到给定通道的一些数据时,它应该检查事件名称,如果存在具有该事件名称的回调,则调用与该事件名称关联的回调。然而,当调用 handleEvent 方法时,通道显然没有回调,即使在 viewDidLoad 的底部它显示为在通道的 callbacks 属性 中有回调。

关于回调在哪里/为什么消失的任何想法?

更新

我现在尝试将通道的定义 ch 移到 channelSetup 函数之外,所以它是这样的,但没有成功:

class ViewController: UIViewController {
    let wSock = wSocket(key: "afa4d38348f89ba9c398")
    var ch: Channel = nil

    func channelSetup() {
        ch = wSock.subscribe("test-channel")
        ch.bind("test-event", { (data: JSON) -> Void in
            println("I'm the callback getting called")
        })
        println(ch.callbacks)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        channelSetup()
    }
}

我已经解决了这个问题,但不是因为 Swift 中发生的事情我不明白。相反,我设置代码的方式意味着创建了重复的通道对象,并且回调仅被添加到其中一个通道。