The right way to write a singleton in Swift

Source: Internet
Author: User
Tags stack trace

This article turns from Cocoachina

This article is translated by Cocoachina translator Leon (community ID) from Krakendev
Original: The right-to-WRITE A SINGLETON
Please maintain the integrity of all content and links.

In the previous post, we talked about how painful state management is, and sometimes it's unavoidable. An example of a state management is familiar to everyone, and that is a singleton. There are many ways to implement a singleton when using Swift, which is a nuisance because we don't know which one is the most appropriate. Here, let's review the history of the Singleton and see how the Singleton is implemented correctly in Swift.

If you want to look directly at the correct implementation of the singleton in Swift, skip to the end of the post.

A single example of the OBJC of past memories

Swift is a natural evolution of objective-c, which implements a single example in the following ways:

12345678910111213141516 @interface Kraken : NSObject@end@implementation Kraken+ (instancetype)sharedInstance {    static Kraken *sharedInstance = nil;    static dispatch_once_t onceToken;        dispatch_once(&onceToken, ^{        sharedInstance = [[Kraken alloc] init];    });    return sharedInstance;}@end

In this off-the-shelf scenario, we can see the basic structure of the singleton. Let's agree on some rules so that we can understand them better.

Single-Instance rule

With regard to the singleton, there are three important criteria to keep in mind:

1. The singleton must be unique (not to say a single case?). Only one such instance can exist in the program life cycle. The existence of a singleton allows us to access the state globally. For example:

Nsnotificationcenter, UIApplication and Nsuserdefaults.

2. To ensure uniqueness of the singleton, the initialization method of the Singleton class must be private. This prevents other objects from creating additional instances from the Singleton class.

3. Taking into account Rule 1, a singleton must be thread-safe to ensure that an instance of the value is created during the lifetime of the program. Concurrency can be complicated sometimes, and simply put, if the code for a single example is incorrect, if you have two threads that instantiate a singleton object at the same time, you might create two singleton objects. In other words, a single case of thread safety must be ensured before it can be guaranteed to be unique. By calling Dispatch_once, the instantiation code is guaranteed to run only once.

It is not difficult to keep singleton uniqueness in the program and initialize it only once. In the remainder of the post, you need to remember that the singleton implementation satisfies the hidden dispatch_once rule.

Swift Single case

Since Swift 1.0, there are many ways to create a singleton. These links are described in more detail, such as

https://github.com/hpique/SwiftSingleton,http://stackoverflow.com/questions/24024549/ Dispatch-once-singleton-model-in-swift and

Https://developer.apple.com/swift/blog/?id=7. But who likes to point links? Let's play a little bit: there are 4 versions in total. Let's take a look at:

1. The ugliest method (Swift skin, objective-c Heart)

123456789101112 class TheOneAndOnlyKraken {    class varsharedInstance: TheOneAndOnlyKraken {        struct Static {            static varonceToken: dispatch_once_t = 0            static varinstance: TheOneAndOnlyKraken? = nil        }        dispatch_once(&Static.onceToken) {            Static.instance = TheOneAndOnlyKraken()        }        returnStatic.instance!    }}

This version is a direct ported version of OBJECTIVE-C. I don't think it looks good because swift should be more concise and descriptive. Don't be a porter, do it better.

2. Structure method ("New bottle of Wine")

12345678 class TheOneAndOnlyKraken {    class varsharedInstance: TheOneAndOnlyKraken {        struct Static {            static let instance = TheOneAndOnlyKraken()        }        returnStatic.instance    }}

At Swift 1.0, static class variables are not supported, and this method is a last resort. But using structs, you can support this feature. Because of the limitations of static variables, we are constrained in such a model. This is better than the objective-c transplant version, but it's not good enough. Interestingly, a few months after the release of Swift 1.2, I can still see this notation. After that, there was more.

3. Global variable Method ("single line Singleton" method)

123456 private let sharedKraken = TheOneAndOnlyKraken()class TheOneAndOnlyKraken {    class varsharedInstance: TheOneAndOnlyKraken {        returnsharedKraken    }}

After Swift 1.2, we have the ability to access control specifiers and static class members (the static classes member). This means that we can finally get rid of chaotic global variables, global namespaces, and namespace collisions. This version looks a little swiftier.

Now, you may have questions: why can't you see dispatch_once? According to the Apple Swift blog, the above methods automatically meet the Dispatch_once rules. Here's a post to prove that the dispatch_once rule has been working.

The lazy initialization method of a global variable (and also a static member of a struct and an enumerator) is called once when it is accessed. Similar to calling ' dispatch_once ' to ensure its initialization of atomicity. So there's a cool ' single call ' approach: just declare a global variable and a private initialization method. "– from Apple's Swift Blog

("The lazy initializer for a global variable (also-static members of structs and enums) are run the first time that Glo Bal is accessed, and are launched as ' dispatch_once ' to make sure that the initialization is atomic. This enables a cool-to-use ' dispatch_once ' in your code:just declare a global variable with an initializer and Mark I T private. " )
This is all the information Apple's official documentation has given us, but it's enough to prove that the static members of the global variables and struct/enumerator are supporting the "dispatch_once" feature. Now, we believe that using global variables to "lazy-wrap" the initialization of a singleton is 100% safe in the Dispatch_once code block. But what about static classes of variables?

This question brings us to more exciting thinking:

The right approach (also known as "single-line singleton") has now been proven correct.

123 class TheOneAndOnlyKraken {    static let sharedInstance = TheOneAndOnlyKraken()}

So far, we have done a lot of research work. This post was inspired by our conversation in Capital One: in the process of pairing programming review code, we tried to find the correct, consistent singleton method used by Swift in the app. We know the right way to write a singleton, but we can't prove it by theory. Without sufficient documentation support, it is futile to prove the correctness of the method. If there is not enough information on the Internet or in the blogosphere, it can only be opinion, and we all know that if we can't find the information online, we won't believe it. This makes me sad.

I searched a lot of information and even turned to more than 10 pages of Google search results, or nothing. Doesn't anyone post proof that the single-line simple interest method is correct? Someone may have sent it, but it's too hard to find it.

So I decided to write the various singleton changes and then add breakpoints to the runtime to observe.

After analyzing the records of each stack trace, I found something interesting-evidence!

Take a look at:

Using Global singleton methods

Use a single-line singleton method

The first picture shows the stack trace when using a global instance. The place to be marked red needs attention. Before invoking the Kraken Singleton, the Swift_once is called, followed by Swift_once_block_invoke. As Apple has previously said in the documentation, "lazy instantiation" of global variables is automatically placed in the Dispatch_once block, and we can assume that this is the thing.

With this knowledge, let's look at the beautiful single-line singleton approach. , the call is exactly the same. Thus, we have the evidence that the single-line singleton method is correct.

Do not forget to set the initialization method to private

@davedelong, Apple's framework preacher, reminded me in good faith that the private nature of the Init method must be guaranteed so that the singleton is truly unique and avoids the need for external objects to create other instances of the Singleton class by accessing the Init method. Since all of the objects in Swift are created by the public initialization method, we need to rewrite our own Init method and set it to private. This is simple and does not break our elegant single-line singleton approach.

1234 class TheOneAndOnlyKraken {    static let sharedInstance = TheOneAndOnlyKraken()    private init() {} //This prevents others from using the default ‘()‘ initializer for this class.}

This ensures that the compiler throws an error when a class attempts to initialize Theoneandonlykraken with ():

That's it, our single-line singleton, very perfect!

Conclusion

Here's a reply to the jtbandes in the "top rated answer to Swift singletons on Stack Overflow" issue in this post: I can't find it either. There are documented benefits of a let statement that can lead to thread safety. I remember that when I joined WWDC last year, there was a similar argument that I could not guarantee that the reader or Googler would occasionally come across this argument. Hopefully this post will help you understand why single-line singleton is the right approach in swift.

The right way to write a singleton in Swift

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.