Detailed explanation of OC single case mode

Singleton mode

Sometimes we need a global object, and we need to ensure that there is only one global object. At this time, we need to use the singleton design mode, but we need to pay attention to that: in the multi-threaded environment, we also need to do a good job in thread protection. In fact, there are many single instances of the system, such as UIApplication, NSNotification, NSFileManager, NSUserDefaults, etc. the following code details

Rigorous single case model in ARC environment

#import <Foundation/Foundation.h>
@interface JHTool : NSObject<NSCopying,NSMutableCopying>
//Class method
//1. Easy access
//2. Identification
//3. Note: share + class name | default + class name | share | default | class name
+(instancetype)shareTool;
@end
#import "JHTool.h"
@implementation JHTool
//Provide a global static variable
static JHTool * _instance;

+(instancetype)shareTool{
    return [[self alloc]init];
}

//allocWithZone is called when alloc is called
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    //Solution 1: add mutex lock to solve the security problem of multi thread access
//    @synchronized(self) {/ / synchronized
//        if (!_instance) {
//            _instance = [super allocWithZone:zone];
//        }
//    }
    //Scheme 2: GCD dispatch [onec], which is thread safe, ensures that the entire program will only be executed once
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}
//rigorous
//Following the NSCopying protocol, you can create objects by copy
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
    return _instance;
}
//The NSMutableCopying protocol is followed, and objects can be created by mutableCopy
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
    return _instance;
}
@end
#import "ViewController.h"
#import "JHTool.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
    JHTool * tool1 = [[JHTool alloc]init];
    JHTool * tool2 = [JHTool shareTool];
    JHTool * tool3 = tool1.copy;
    JHTool * tool4 = tool2.mutableCopy;
    NSLog(@"tool1:%p,tool2:%p,tool3:%p,tool4:%p,",tool1,tool2,tool3,tool4);
    printf("tool1:%p,tool2:%p,tool3:%p,tool4:%p,",tool1,tool2,tool3,tool4);

}
@end

Print results:
ARC

Rigorous single case mode in MRC environment

After Xcode5, the project defaults to ARC, so set the project to MRC environment, select project target - > build stings - > All - > Search 'Automatic' - > set Objective-C Automatic Reference Counting to NO, as shown below:

MRC single mode code details

#import <Foundation/Foundation.h>
@interface HJTool : NSObject<NSCopying,NSMutableCopying>
//Class method
+(instancetype)shareTool;
@end
#import "HJTool.h"

@implementation HJTool
//Modify the environment to MRC: select project target - > build destinations - > All - > Search 'Automatic' - > set Objective-C Automatic Reference Counting to NO
//Provide a static global variable
static HJTool * _instance;
//Implementation class method
+(instancetype)shareTool{
    return [[self alloc]init];
}
//alloc will call allocWithZone
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    //Method 1. Mutex to ensure thread safety
//    @synchronized(self){
//        if (_instance == nil) {
//            _instance = [super allocWithZone:zone];
//        }
//    }
    //Method 1. GCD - > dispatch ﹣ once ﹣ this method can only be executed once, which is thread safe
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}
-(id)copyWithZone:(NSZone *)zone{
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
    return _instance;
}
//MRC specific
-(instancetype)retain{
    return _instance;
}
-(oneway void)release{
    NSLog(@"%zd",_instance.retainCount);
}
#import "ViewController.h"
#import "HJTool.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor yellowColor];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    HJTool * t1 = [[HJTool alloc]init];
    HJTool * t2 = [[HJTool alloc]init];
    HJTool * t3 = [HJTool shareTool];
    HJTool * t4 = [t1 copy];
    HJTool * t5 = [t2 mutableCopy];
    HJTool * t6 = [t1 retain];
    NSLog(@"t6.retainCount : %zd",t1.retainCount);
    NSLog(@"t1:%p t2:%p t3:%p t4:%p t5:%p t6:%p",t1,t2,t3,t4,t5,t6);
}

Print results

Extension: macro to distinguish MRC from ARC

#if __has_feature(objc_arc)
    //If the condition meets ARC, the code of ARC can be processed
    NSLog(@"ARC");
#else
    //If the condition meets MRC, the MRC code can be processed
    NSLog(@"MRC");
#endif

General single instance mode

Define a macro in SingleDog.h file, class method declaration in SingleH(name) file, and method in SingleM(name) file. The code of the defined macro is as follows

#define SingleH(name) +(instancetype)share##name;//. h file replacement
//. m file replacement
#if __has_feature(objc_arc)//arc
//Condition meets ARC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}

#else
//Conditions meet MRC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
return _instance;\
}
#endif

For a single SingleTool file, the header file and the implementation file call the singleh (name) and singlem (name) of the macro respectively. The code is as follows

#import <Foundation/Foundation.h>
#import "SingleDog.h"

@interface SingleTool : NSObject
//Replace header file
SingleH(SingleTool)

@end
#import "SingleTool.h"

@implementation SingleTool
SingleM(SingleTool)
@end
#import "ViewController.h"
#import "SingleTool.h"

@interface ViewController ()

@end

@implementation ViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    SingleTool * t1 = [[SingleTool alloc]init];
    SingleTool * t2 = [SingleTool shareSingleTool];
    SingleTool * t3 = [t1 copy];
    SingleTool * t4 = [t1 mutableCopy];
#if __has_feature(objc_arc)
    //If the condition meets ARC, the code of ARC can be processed
    NSLog(@"ARC");
#else
    //If the condition meets MRC, the MRC code can be processed
    NSLog(@"MRC");
    SingleTool * t5 = [t1 retain];
    NSLog(@"t6.retainCount : %zd",t1.retainCount);
    NSLog(@"t1:%p t2:%p t3:%p t4:%p t5:%p",t1,t2,t3,t4,t5);
#endif
}

@end

The printing results in MRC environment are as follows:
MRC

Posted by dotBz on Tue, 28 Apr 2020 09:03:18 -0700