Source code

Application of single case in iOS development

Translation and modification from

Link: Avoiding Singleton Abuse


Single case (Singletons) is one of the core modes of Cocoa. On iOS, singletons are very common, such as UIApplication, NSFileManager and so on. Although they are very convenient to use, there are actually many problems to be noted. So when you complete the dispatch_once code fragment next time, think about the consequences.

What is a single case?

In the book "design patterns", the definition of singleton is given.

Singleton mode To ensure that a class has only one instance and to provide a global access point to access it.

The singleton mode provides an access point for customers to generate unique instances for shared resources and access shared resources through it. This mode provides flexibility.

In Objective-C, you can use the following code to create a singleton:

 + (instancetype) sharedInstance
static dispatch_once_t once; 
static ID sharedInstance; 
dispatch_once (&once, ^{
sharedInstance = [[self;});

When a class can only have one instance and must access it from an access point, it is very convenient to use single examples, because the use of singletons ensures that the access points are unique, consistent and well known.

Problems in singletons

Global state

First of all, we should all reach a consensus that "global variable state" is dangerous because it makes the program difficult to understand and debug, and on the reduction of state codes, object-oriented programming should learn from functional programming.

For example, the following code:

 @implementation Math{
NSUInteger _a; 
NSUInteger _b;}} - (NSUInteger) computeSum
return _a + _b;}

This code wants to calculate the sum of _a and _B and return it. But in fact, there are many problems in this code.

  • _a and _b are not used as parameters in the computeSum method. Compared with finding interface and knowing which variable control method's output, finding implementation is more covert, and hiding is easier to erroneous.

  • When preparing to modify the values of _a and _b to let them call the computeSum method, programmers must clearly modify their values without affecting the correctness of other codes containing two values, and it is particularly difficult to make such judgments in multithreaded situations.

Compare the following code:

 + (NSUInteger) computeSumOf: (NSUInteger) a plus: (NSUInteger) b
return a + B;}

In this code, the affiliation of a and B is very clear and no longer needs to be changed. Instance state To invoke this method, and do not worry about the side effects of calling this method.

The example and Single case What does it matter? In fact, Single case It's sheepskin. Global state A singleton can be used everywhere, and it does not need to clearly declare subordinations. Any module in the program can simply call [MySingleton sharedInstance] and get the access point of this single instance, which means that any side effects that occur when interacting with a singleton may affect a random piece of code in the program, such as:

 @interface MySingleton: NSObject

+ (instancetype) sharedInstance; NSUInteger; badMutableState; void (void) setBadMutableState: (NSUInteger) badMutableState; setBadMutableState: (());

In the above code, ConsumerA and ComsumerB are two completely independent modules in the program, but the method in ComsumerB will affect the behavior in ComsumerA, because the change of this state is passed through the singleton.

In this code, it is precisely because of the global and state of the singleton, which leads to the implied coupling between the two seemingly unrelated modules of ComsumerA and ComsumerB.

Object life cycle

Another major problem of singletons is their lifecycle.

For example, suppose that a app needs to be able to let users see their friends list function. Every friend has his own head. At the same time, we hope that app can download and cache these friends' heads. At this point, by learning the knowledge of singletons before, we will probably write the following code:

 @interface MyAppCache: NSObject

+ (instancetype) sharedCMyAppCache; NSData - (void) cacheProfileImage: (NSData *) imageData forUserId: (NSString *); imageData - (* *) ((*)) (* *)

This code looks completely without problems and runs well, so app continues to develop until one day, we decide to help app join the "logout" function. Suddenly, we found that user data was stored in the global singleton. When the user logs out, we want to clear these data and create a new MyAppCache for the new user when he logs in.

But the problem is in singleton, because the definition of singleton is: "create once, live forever". In fact, there are many ways to solve the above problem. We may destroy this single case when the user logs out.

 Static MyAppCache *myAppCache; *myAppCache + (instancetype) sharedMyAppCache
if (myAppCache) 
myAppCache = [[self alloc] init]; 
return myAppCache;}} + + (x) = =;}

The above code distorts the singleton pattern, but it works.

In fact, this method can be used to solve this problem, but it costs too much. The most important point is that we gave up the dispatch_once, which ensured the thread safety of the method invocation. Now all the codes that call [MyAppCache shareMyAppCache] will get the same variable, and need to clearly use the order of MyAppCache code execution. Just imagine that when the user came out, he happened to call this method to save the picture.

On the other hand, to implement this method, we need to make sure that the tearDown method is not invoked when the background task is not finished, or that the background tasks will be canceled when the tearDown method is implemented. Otherwise, another new MyAppCache will be created and the old data will be saved.

However, because a single case does not have a clear owner (because a single case manages its own life cycle by itself), it is very difficult to destroy a single case.

So then you might think, " Then don't make MyAppCache single case. In fact, the problem is that the life cycle of an object may not be well defined at the beginning of the project. If the life cycle of an object will match the life cycle of the whole program, it will greatly limit the scalability of the code, and it will be painful when the product demand changes.

So everything above is to illustrate a point: " The singleton should only maintain the overall state, and the life cycle of the state is consistent with the life cycle of the program. " For the singletons already existing in the program, critical review is necessary.

Not conducive to testing

This part is mentioned in the last chapter, but I think testing is a very important part in software development, so we separate the content of this part into another chapter and add some personal opinions.

Because singletons have been alive throughout the life cycle of app, they have been alive even during the execution of tests, resulting in a test that may affect another test, which is a taboo in unit testing.

Therefore, it is necessary to effectively destroy a single case during the unit test and maintain the thread safety characteristics of a single case. But I mentioned in the above:

"But because a single case does not have a clear owner (because a single case manages its own life cycle), destroying a single case is very difficult. "

It seems that the two are self contradictory, but in fact, they can choose to simplify the single case. Real "Single case ServiceRegistry, and the other" Potential The single case is quoted by ServiceRegistry, so that the other singletons have a owner, which can destroy the singletons in time for unit testing, and ensure the independence of unit tests.

On the other hand, the existence of ServiceRegistry makes other "singletons" no longer singletons, so that in TDD, the single case that is difficult to mock will become simpler mock.


We all know that the global variable state is not good, but when we use singletons, we inadvertently turn it into a global variable that we hate.

In object oriented programming, we need to reduce the scope of variable states as much as possible, while singletons run counter to this idea, hoping to think more about the next single case, and consider whether this variable is really worth being a singleton. If not, use the dependency injection mode instead.

For more information, check out my home page.

Author: Dywane


Fabulous ( Zero )

This article is composed of Contributor Creation, article address: Https://
Use Knowledge sharing signature 4 The international license agreement is licensed. In addition to the reprint / provenance, all originals or translations of this site must be signed before retransmission. The final edit time is April, 22, 2019 at 10:30 pm.

Hot articles