问题
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