【问题标题】:Lazy vs init for singleton design in swiftswift中单例设计的惰性vs init
【发布时间】:2017-11-18 07:11:52
【问题描述】:

我正在使用 Socket.io 库向我的应用程序添加实时功能,并且我正在使用单例设计

import SocketIO

class SocketIOManager: NSObject {

    static let sharedInstance = SocketIOManager()

    var socket: SocketIOClient!

    func establishConnection() {
       socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
       socket.connect()

    }

    func closeConnection() {
        socket.disconnect()
    }

}

所以从技术上讲,每当我的应用程序加载时,socket 不能为零,否则它会崩溃。我找到了两个可以解决这个问题的解决方案

  1. 懒惰

    lazy var socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
    

通过使用这个方法socket 永远不会为零,因为它总是会被初始化。但是这种方法的问题是,如果创建 SocketIOClient 并不便宜,那么它会是一个很好的方法。

  1. 初始化()

    class SocketIOManager: NSObject {
    
        static let sharedInstance = SocketIOManager()
    
        let socket: SocketIOClient!
    
    
    
    init() {
            super.init()
            socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
      }
    }
    

通过使用这种方法,我有点困惑,因为我最初并没有像这样创建 SocketIOManager 对象

let socket = SocketIOManager()

因为如果我没记错的话,如果只创建对象,套接字就会被初始化,而且我不确定在单例设计中使用init()

哪种方法适合我的用例?

【问题讨论】:

    标签: ios swift socket.io


    【解决方案1】:

    在 Swift 全局变量中,包括类的静态成员默认是 lazy 的,所以这是正确的:

    class SocketIOManager: NSObject {
    
        static let sharedInstance = SocketIOManager()
    
        let socket: SocketIOClient!
    
        private override init() {
            super.init()
            socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
      }
    }
    

    注意 init 上的 private 以防止其他人尝试实例化此类,因为它是一个单例。

    【讨论】:

    • 我收到了这个错误Property 'self.socket' not initialized at super.init call
    • socket = SocketIOClient(soc.... 需要在super.init() 之上,然后socket 不需要被隐式解包。编辑:假设 SocketIOClient(socketURL: config) 不是一个失败的初始化器。
    【解决方案2】:

    来自documentation

    存储类型属性在首次访问时被延迟初始化。它们保证只初始化一次,即使被多个线程同时访问,也不需要用惰性修饰符标记。

    推荐的方式是声明socket为非可选,先初始化属性再调用super

    class SocketIOManager: NSObject {
    
        static let sharedInstance = SocketIOManager()
    
        let socket: SocketIOClient
    
        init() {
            socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
            super.init()
        }
    }
    

    或者使用闭包初始化socket,但这要求不能访问同一类的其他属性。

    class SocketIOManager: NSObject {
    
        static let sharedInstance = SocketIOManager()
    
        let socket: SocketIOClient = {
             return SocketIOClient(socketURL: URL(string: "https://server.com/path")!, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
        }()
    }
    

    或者如果mainURL 在同一个类中被声明为常量,你可以懒惰地初始化socket。这也意味着套接字将在第一次访问时被初始化。

    class SocketIOManager: NSObject {
    
        static let sharedInstance = SocketIOManager()
    
        let mainURL = URL(string: "https://server.com/path")!
    
        lazy var socket: SocketIOClient = {
             return SocketIOClient(socketURL: mainURL, config: [.log(false), .compress, .connectParams(["token": "asdasdasdsa"])])
        }()
    }
    

    在第二种和第三种情况下,不需要额外的init 方法。

    【讨论】:

    • 如果getToken() 不存在,那么它将不会建立连接,因为最初加载套接字时没有令牌。如何重新初始化套接字?
    • 没有人能读懂你的想法。 getToken() 是什么?如果您需要临时连接,则单例可能是错误的解决方案。或者你必须创建套接字var 并按需创建一个新的。
    • "token".connectParams 的 init 函数中。我不喜欢用错词
    猜你喜欢
    • 1970-01-01
    • 2018-06-11
    • 2017-02-27
    • 2019-06-17
    • 1970-01-01
    • 2016-01-22
    • 2017-10-29
    • 2014-11-20
    • 1970-01-01
    相关资源
    最近更新 更多