标签云

微信群

扫码加入我们

WeChat QR Code

I'm trying to work out an appropriate singleton model for usage in Swift. So far, I've been able to get a non-thread safe model working as:class var sharedInstance:TPScopeManager {get {struct Static {static var instance : TPScopeManager? = nil}if !Static.instance {Static.instance = TPScopeManager()}return Static.instance!}}Wrapping the singleton instance in the Static struct should allow a single instance that doesn't collide with singleton instances without complex naming schemings, and it should make things fairly private. Obviously though, this model isn't thread safe, so I tried to add dispatch_once to the whole thing:class var sharedInstance:TPScopeManager {get {struct Static {static var instance : TPScopeManager? = nilstatic var token : dispatch_once_t = 0}dispatch_once(Static.token) { Static.instance = TPScopeManager() }return Static.instance!}}But I get a compiler error on the dispatch_once line:Cannot convert the expression's type 'Void' to type '()'I've tried several different variants of the syntax, but they all seem to have the same results:dispatch_once(Static.token, { Static.instance = TPScopeManager() })What is the proper usage of dispatch_once using Swift? I initially thought the problem was with the block due to the () in the error message, but the more I look at it, the more I think it may be a matter of getting the dispatch_once_t correctly defined.


I would remove all that static code and use a readonly property with a lazy initializer.

2019年06月26日01分47秒

That's what I meant. Unfortunately we still don't have enough information about the internals. However, IMHO any implementation of lazy should be thread safe.

2019年06月26日01分47秒

And this way also has the advantage of not exposing the implementation to the predations of callers.

2019年06月25日01分47秒

It also doesn't seem like you can have lazy class variables.

2019年06月25日01分47秒

Be careful! Two things to note with this approach. First, any classes that inherit from this will have to override the sharedInstance property. Static.instance = TPScopeManager() forces the instance type. If you use something like Static.instance = self() with a required initializer, the appropriate type class will be generated. Even so, and this is the important thing to note, only once for all instances in the hierarchy! First type to initialize is the type set for all instances. I don't think objective-c behaved the same.

2019年06月25日01分47秒

"thread safe by virtue of let" — has this been stated anywhere? I can't find mention of it in the documentation.

2019年06月26日01分47秒

jtbandes Constants are thread safe in all the languages I know.

2019年06月25日01分47秒

DaveWood I assume you're talking about the last approach. I'll quote myself: "I'd say it's no longer necessary to use this approach but I'm putting it here anyway as I find the differences in syntax interesting."

2019年06月25日01分47秒

Should init be also be declared private to guarantee one and only one instance of the object will ever exist throughout the app's lifetime?

2019年06月26日01分47秒

In the "Class constant" approach, I'd suggest (a) declaring the class to be final so you don't subclass it; and (b) declaring the init method to be private so that you can't accidentally instantiate another instance somewhere.

2019年06月26日01分47秒

To confirm: global variables have lazy, thread-safe initialization, but class variables don't. Right?

2019年06月25日01分47秒

I would add that a good practice would be to declare the initializer as private: private init() {}, to further enforce the fact that this class is not meant to be externally instantiated.

2019年06月26日01分47秒

so static struct var initialization is lazy and thread safe, what if that static struct var is a dictionary for multitons, then we have to manually synchronize/queue calls to it for each access, right?

2019年06月26日01分47秒

If I understand your question correctly, dictionary and array accesses are not inherently thread-safe, so you will need to use some form of thread synchronization.

2019年06月25日01分47秒

DavidBerry How should I call a function inside this singleton class? I need a function to be called on the first call of myClass.sharedInstance.

2019年06月25日01分47秒

Anyone reading only this answer: Remember to make the token static, otherwise the behavior is undefined. See David's edited question for the complete code.

2019年06月26日01分47秒

nschum otherwise, the behaviour is not undefined, it is just broken in a well-defined way: the block will always execute.

2019年06月25日01分47秒

Michael: The documentation states it is undefined. The current behavior is therefore coincidental.

2019年06月25日01分47秒

That's an odd thing to say. If the documentation calls it "undefined" that just means whoever wrote the code doesn't make any promises to what it does. It has nothing to do with the code knowing if the variable is static. It just means that the current (or apparent) behavior cannot not be relied upon.

2019年06月26日01分47秒

You might want to add private init() {} as initialiser of SingletonClass. to prevent instantiate from outside.

2019年06月25日01分47秒

Why a "var" and lot a "let"?

2019年06月25日01分47秒

maybe could be a let, I only tested it out with a var.

2019年06月25日01分47秒

I like this answer, however I need to access this (Singleton) from Interface Builder. Any idea on how could I access this tpScopeManagerSharedInstance from within IB?. Thanks.-

2019年06月25日01分47秒

This is my preferred way of having a singleton. It has all the usual features (thread-safety & lazy instantiation) and it supports a very lightweight syntax: no need to write TPScopeManager.sharedInstance.doIt() all the time, just name your class TPScopeManagerClass, have this declaration next to the class public let TPScopeManager = TPScopeManagerClass(), and when using just write TPScopeManager.doIt(). Very clean!

2019年06月26日01分47秒

There's nothing here to prevent creation of additional instances of TPScopeManager, and it is therefore not a singleton by definition.

2019年06月26日01分47秒

upvoted for the comment of LearnCocos2D :) , also for the style.

2019年06月26日01分47秒

the global variable should be changed to a class variable via a static inside the class.

2019年06月25日01分47秒

malhal when a variable is marked private but outside a class, it's not global - but scoped only to the file it is in. A static inside the class would work pretty much the same, but i've updated the answer to use the static as you suggested, as it better groups the variable to the class if you happen to use multiple classes within the file.

2019年06月25日01分47秒

"Swift Singletons are exposed in the cocoa frameworks as class functions" ... Not in Swift 3. They're now usually static properties.

2019年06月26日01分47秒

how can you set the static let instance back to nil?

2019年06月25日01分47秒

user1463853 - You can't, and generally shouldn't.

2019年06月26日01分47秒

this needs final class, can you explain more the difference, coz I have issue with the other solution of singleton with struct

2019年06月26日01分47秒

should that be private override init() {}

2019年06月26日01分47秒

I am pretty sure that just using this default static syntax will do the all annoying jobs.

2019年06月25日01分47秒

unfortunately statics only work inside of structs, so that's why this pattern.

2019年06月26日01分47秒

My intention was that we don't have to use dispatch_once stuffs.I am betting on your style. :)

2019年06月25日01分47秒

I like this solution, but is it thread-safe?

2019年06月25日01分47秒

Isn't class within a class declaration the equivalent of static in a struct declaration?

2019年06月25日01分47秒

This is actually more concise & correct than the other example because it is implemented the same way as other Swift singletons are. i.e: as class functions like NSFileManager.defaultManager(), but still uses the lazy thread-safe static member mechanisms of Swift.

2019年06月26日01分47秒

Cocoa generally implements these as static properties, nowadays, not as class functions.

2019年06月26日01分47秒

I am aware of that, my comment is over 2 years old. Thanks for mentioning.

2019年06月25日01分47秒

Well done for not only marking init as private, but also for making the sharedMyModel as final! For the sake of future readers, in Swift 3, we might be inclined to rename sharedMyModel to be simply shared.

2019年06月25日01分47秒

This is the only correct answer, except that the override and call to super.init are erroneous and will not even compile.

2019年06月26日01分47秒

This is exactly the same as one of the answers I went through on the way to the current answer.Since global variables are initialized both lazy and thread-safe, there's no reason for the additional complexity.

2019年06月26日01分47秒

David Other than not having a global variable. :)

2019年06月25日01分47秒

hpique no, exactly like one of my earlier attempts.Look at the edit history.

2019年06月26日01分47秒

Why a NSObject subclass?. Apart from that, this seems to beessentially the same as stackoverflow.com/a/28436202/1187415.

2019年06月25日01分47秒

IMO, this is the only correct Swift way to implement Singleton. other answers are ObjC/C/C++ way

2019年06月25日01分47秒

Could you elaborate on this answer? It's not clear to me where Singleton is instantiated from this snippet

2019年06月25日01分47秒

KennyWinker I don't have an Apple developer login, therefore no swift and so I can't answer when the initialisation occurs. In Java, it is on first use. Perhaps you could try it with a print on initialization and see if the print occurs at launch or after access. It will depend on how enum is implemented by the compiler.

2019年06月26日01分47秒

KennyWinkler: Apple have just clarified how this works, see developer.apple.com/swift/blog/?id=7. In it they say "run the initializer for a global the first time it is referenced, similar to Java" and in particular. They also say that under the covers they are using"dispatch_once to make sure that the initialization is atomic". Therefore enum is almost certainly the way to go unless you have some fancy init to do, then a private static let is the solution.

2019年06月26日01分47秒

Why did you use the closure initialization while you can directly use static let sharedInstance = Singleton()

2019年06月25日01分47秒

if you do not want to do any additional setup then what you saying is right.

2019年06月26日01分47秒

As I say in my comments, the only reason to do it is that at some point in the future you can move/hide the global variable and get more singleton-like behavior.At that point, if everything is using a consistent pattern, you can just change the singleton classes themselves without having to change the usage.

2019年06月25日01分47秒

Thanks for the answer, but this really doesn't add anything to the previous answers that are several years old.

2019年06月26日01分47秒

As has been discussed at great length here, it's not necessary in swift to wrap initialization in dispatch_once since static variable initialization is lazy and automatically protected via dispatch_onceApple actually recommends using statics instead of dispatch_once for that reason.

2019年06月25日01分47秒

What is the advantage over a static type property (which is guaranteed to be lazily initialized only once, even when accessed across multiple threads simultaneously) ?

2019年06月26日01分47秒

How is that different from previous answers like stackoverflow.com/a/28436202/1187415, stackoverflow.com/a/33758825/1187415, or stackoverflow.com/a/34996627/1187415?

2019年06月25日01分47秒

private init was already suggested here: stackoverflow.com/a/28436202/1187415.

2019年06月26日01分47秒