👨 🏻 💻 making Demo

Easy to remember:

  • Singleton: Ensure that a class has only one instance and that it instantiates and makes this instance singleton available to the entire system (by taking precautions against creating other instances)
  • Security: To prevent two threads from calling shareInstance at the same time, use @synchronized locking
  • The dispatch_once_t type of dispatch_once is typedef long
    • OnceToken = 0, the thread executes code in the block of dispatch_once
    • OnceToken = -1, the thread skips the dispatch_once block and does not execute the code
    • OnceToken = other value, the thread is blocked by the thread, waiting for the onceToken value to change
  • Purpose: Limit creation, provide global calls, save resources and improve performance
  • Common application scenarios:
    • UIApplication
    • NSNotificationCenter
    • NSFileManager
    • NSUserDefaults
    • NSURLCache
    • NSHTTPCookieStorage

citation

The Design Patterns: Elements of Reusable Object-oriented Software (Design Patterns) is the work of Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides Co-authored (Addison-Wesley, 1995). These authors are often referred to as the Gang of Four.

The GoF example referenced at the beginning helps you understand the creative design pattern — singletons

SINGLETON — I have 6 beautiful wives, all of whom are married to me. I am our SINGLETON. Whenever they say “husband”, they all refer to the same person, that is me. (I had a dream just now, nothing so good.)

Singleton pattern: The singleton pattern ensures that there is only one instance of a class, and it instantiates itself and provides this instance singleton pattern to the entire system. The singleton pattern should only be used when there is a need for a true “single instance.”

Creation of a singleton

1. Single thread singleton

We know that for a singleton class, we have to set aside an interface to return the generated singleton. Since there can only be one instance in a class, we create the instance the first time we access it, and then access it directly from the already created instance

@implementationSingleton
+ (instancetype)shareInstance{
    staticSingleton* single;
    if(! single) { single = [[Singleton alloc] init]; }return single;
}
@end
Copy the code

Ps: Strictly speaking, we also need to seal the alloc method, because strict singletons are not allowed to create other instances, and the Alloc method can generate arbitrary instances externally. But given that alloc is an NSObject, there’s no way to make alloc private in iOS. The best you can do is to overwrite the alloc and let it return null, which can lead to other problems for people using the interface. So normally we don’t do anything special with alloc. The singleton of the system also does nothing to alloc

2. @ synchronized singleton

We generally can’t guarantee that an instance will be used in single-threaded mode, so we have to accommodate multithreading. The above singleton creation approach can be problematic in multithreaded situations. If two threads call shareInstance at the same time, it is possible to create two singles. So in multithreaded cases, we need to use @synchronized to lock.

@implementationSingleton
+ (instancetype)shareInstance{
    staticSingleton* single;
    @synchronized(self) {
        if(!single) {           
            single = [[Singleton alloc] init];       
        }    
    }
    return single;
}
@end
Copy the code

That way, when multiple threads call shareInstance at the same time, only one thread can enter to create a single because @synchronized is already locked. In this way, the problem of multithreading call singleton is solved

3. Dispatch_once singleton

Using @synchronized solves the multithreading problem, but it’s not perfect. The lock is only necessary if the single is not created. If single is already created. Not only is locking not beneficial, it also affects the performance of program execution (when multiple threads execute code in @synchronized, only one thread executes and the others wait). So is there a way to solve the problem without affecting performance? This method is dispatch_once in GCD

+ (SingletonManager*)shareManager {
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        if(defaultManager == nil) {
            NSLog(@"dispatch_once Token: %ld",token);
            defaultManager = [[selfalloc] init]; }});NSLog(@"Token: %ld",token);
    NSLog(@"DefaultManager: %@",defaultManager);
    return defaultManager;
}
Copy the code

Print the result

How can dispatch_once solve simultaneous multithreading problems without affecting performance?

Here’s how dispatch_once works:

Dispatch_once determines how to execute code based on the onceToken value.

  • When onceToken= 0, the thread executes the code in the block of dispatch_once
  • When onceToken= -1, the thread skips the dispatch_once block and does not execute the code
  • When onceToken is another value, the thread is blocked by the thread waiting for the onceToken value to change

When a thread calls shareInstance first and executes the code in the block, it first needs to change the value of onceToken before executing the code in the block. Here the onceToken value becomes 768.

When another thread retrieves the value of onceToken, the value will already be 768. Other threads are blocked.

When the block thread finishes executing the block. The onceToken changes to -1. Other threads stop blocking and skip the block.

The next time shareInstance is called, block will already be -1. Skip the block.

In this way, dispatch_once synchronizes the blocking thread on the first call and does not block the thread after the singleton is generated.

Problems encountered: Thread 1 and thread 2 are both calling shareInstance to create a singleton, so thread 1 runs to if (_instance == nil) and finds _instance = nil, so it initializes a _instance, assuming thread 2 runs to if, Thread 1 hasn’t created instance _instance yet, so _instance = nil is still valid, and thread 2 will create _instace again.

Although mutex can also be used to solve the problem of simultaneous creation of multiple threads, dispatch_once is the most efficient and secure solution to this problem.

The macro method creates a singleton

H for macro definition

// Singleton.h
#import <Foundation/Foundation.h>
#define SingletonH(name) + (instancetype)shared##name;
#define SingletonM(name) \
static id _instance; \
\
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [superallocWithZone:zone]; The \}); \return_instance; \} \ \ + (instancetype)shared##name \{\static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[selfalloc] init]; The \}); \return_instance; \} \ \ - (id)copyWithZone:(NSZone *)zone \
{ \
return_instance; \}\ \ - (id)mutableCopyWithZone:(NSZone *)zone { \
return_instance; The \}@interface Singleton : NSObject
@end
Copy the code

Use the method viewController.h

// ViewController.h
#import <UIKit/UIKit.h>
#import "Singleton.h"/ / macro method
@interface ViewController : UIViewController
SingletonH(viewController)
@end
Copy the code

ViewController. M

SingletonM(ViewController)
- (void)viewDidLoad {
    [super viewDidLoad];
    // Call macro to define a singleton
    NSLog(@" Address print: \n%@\n%@\n%@\n%@", [ViewController sharedViewController], [ViewController sharedViewController], [[ViewController alloc] init], [[ViewController alloc] init]);
}
Copy the code

Print the result

The printed results show the same address

The use of singletons

1) The singleton pattern is used to restrict a class to creating only one object, so that the properties of this object can store globally shared data. All classes can access and set the property data in this singleton;

2) If a class is very expensive to create or affects performance, the object can be set as a singleton to save resources and improve performance.

The singleton class ensures that there is only one instance object of the class in the life cycle of the application and that it is easily accessible to the outside world.

A singleton class used in iOS

  • UIApplication
  • NSNotificationCenter
  • NSFileManager
  • NSUserDefaults
  • NSURLCache
  • NSHTTPCookieStorage

Analysis of the dispatch_once mechanism

In IOS development, in order to ensure that the singleton is initialized only once in the entire program run, single thread, through static variables can be achieved; But the advent of multithreading makes it possible for singletons to return different objects under extreme conditions described above. If multiple processes access the singleton at the same time before the singleton initialization is complete, they may all acquire different singleton objects.

Multithreaded protection under the singleton initialization code

+ (instancetype)defaultObject{
    static SharedObject *sharedObject = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedObject = [[SharedObject alloc] init];
    });
    return sharedObject;
}
Copy the code

Click dispatch_once_t and see it’s a typedef long

A static variable is initialized only once during program execution, and then the next time it is accessed, its value is the same as the last time, and it cannot be directly modified anywhere except for this initialization method. This is the premise that singletons are initialized only once.

Internal macro converts _dispatch_once to dispatch_once

Looking at the _dispatch_once function, we find the DISPATCH_EXPECT method

~0l is the inverse of the long integer 0, which is -1

GNUC just stands for the main version of GCC, we ignore it, we just have DISPATCH_EXPECT(x, v), DISPATCH_EXPECT(*predicate, ~ 0L) which means the *predicate is probably ~ 0L, And when DISPATCH_EXPECT(*predicate, ~0l) is not ~0! Then the real dispatch_once function is called.

The first time you run the predicate, the default value of the predicate is 0. Logically, if two processes run the dispatch_once method at the same time, both processes get 0. Eventually both processes will call the original dispatch_once function.

From this I stick the above rules again, you can see their own debugging

  • When onceToken= 0, the thread executes the code in the block of dispatch_once
  • When onceToken= -1, the thread skips the dispatch_once block and does not execute the code
  • When onceToken is another value, the thread is blocked by the thread waiting for the onceToken value to change

The above articles are compiled from: http://www.cocoachina.com/ios/20160907/17497.html, https://www.jianshu.com/p/160d77888443, https://blog.csdn.net/mlibai/article/details/46945331