Converting Swift 3 code without use of dispatch_once_t

倾然丶 夕夏残阳落幕 提交于 2019-12-08 12:50:49

问题


Prior to migrating to Swift 3, I had the following code:

//Set up singleton object for the tracker
class func setup(tid: String) -> WatchGATracker {
    struct Static {
        static var onceToken: dispatch_once_t = 0
    }
    dispatch_once(&Static.onceToken) {
        _analyticsTracker = WatchGATracker(tid: tid)
    }
    return _analyticsTracker
}

I get the following error:

'dispatch_once_t' is unavailable in Swift: Use lazily initialized globals instead

Apparently, the conversion tool converted the code to this:

 class func setup(_ tid: String) -> WatchGATracker {
    struct Static {
        static var onceToken: Int = 0
    }
    _ = WatchGATracker.__once
    return _analyticsTracker
}

And at the top of my class, it added this:

private static var __once: () = {
        _analyticsTracker = WatchGATracker(tid: tid)
}()

But I still get an error:

Instance member 'tid' cannot be used on type 'WatchGATracker'

tid is declared as:

fileprivate var tid: String

It used to be declared as:

private var tid: String

I cannot seem to figure out how to fix my code, does anyone have any suggestions?


回答1:


It's unclear what you expect this code to do. What should happen if someone calls WatchGATracker.setup(tid: "abc") and then later calls WatchGATracker.setup(tid: "123")? It seems the latter tid is quietly ignored. I don't believe this is a correct singleton.

You need to be more explicit about what correct behavior is. For example, if it is a programming error not to call setup before using the tracker, you want something like this:

class WatchGATracker {
    static private(set) var instance: WatchGATracker!

    class func setup(tid: String) {
        precondition(instance == nil)
        instance = WatchGATracker(tid: tid)
    }

    init(tid: String) { . . . }
}

If different parts of the program may call with different tids, then you should do something more like:

static private var instances: [String: WatchGATracker] = [:]

class func setup(tid: String) -> WatchGATracker {
    if let instance = instances[tid] {
        return instance
    }

    let instance = WatchGATracker(tid: tid)
    instances[tid] = instance
    return instance
}

(As noted in the comments, if you want thread-safety for this fetch, then you'll need to add a class-level dispatch queue to manage it in the same way that you would add an instance-level dispatch queue to handle thread-safety at that level. You don't get automatic thread safety once initialization requires a parameter.)




回答2:


When the error says "Use lazily initialized globals instead", it's suggesting something like:

class WatchGATracker {
    private var tid: String
    static let shared = WatchGATracker(tid: "foo")
    private init(tid: String) { ... }
    ...
}

Or, if you really want to give the caller a chance to set the tid (which is curious pattern for a singleton), change init to not take a parameter, make tid implicitly unwrapped but not private at all, and declare shared like:

class WatchGATracker {
    var tid: String!
    static let shared = WatchGATracker()
    private init() { ... }
    ...
}

and then you can do

WatchGATracker.shared.tid = "foo"

By the way, regarding fileprivate, it's suggesting that merely because that's what the old private now translates to. But unless you've got some reason it needs to be fileprivate, I'd probably switch it back to private (which now makes it private to the lexical scope).




回答3:


We've all gotten used to over-complicated singleton patterns... It's actually very simple now:

class WatchGATracker {
    static let sharedInstance = WatchGATracker()
    private override init() {}
}

Source: http://krakendev.io/blog/the-right-way-to-write-a-singleton

As for the setup() function, I agree with @Rob Napier above in his answer but I'll go a step further. If you're trying to reconfigure a singleton you're doing it wrong. If you have some required setup parameter that varies from one usage to another you should create separate instances.

Maybe there's some kind of connectivity or other function you're trying to reuse that's leading you down the singleton path but if that's the case you should extract just that function to it's own class and let these configurable instances share that singleton.

/// Watch Connection singleton
class WatchConnection: NSObject  {
    static let sharedInstance = WatchConnection()
    private override init() {}

    func doSomething() {}
}

//Watch tracker class for each instance of a watch
class WatchGATracker {
    init(tid: String) {
        //do something useful
    }

    let connection = { WatchConnection.sharedInstance }()
}

let one = WatchGATracker(tid: "one")
one.connection.doSomething()


来源:https://stackoverflow.com/questions/39576848/converting-swift-3-code-without-use-of-dispatch-once-t

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!