OC 的单例也是比较让人蛋疼的.
ToolManager.h
#import <Foundation/Foundation.h>@interface ToolManager : NSObject@property (copy, nonatomic) NSString *tName;+ (ToolManager *)sharedToolManager;@end
ToolManager.m
#import "ToolManager.h"@implementation ToolManager+ (ToolManager *)sharedToolManager{static ToolManager *manager;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{manager = [[ToolManager alloc] init];});return manager;}- (instancetype)init{if (self = [super init]) {}return self;}上面使用 GCD 实现单例.
你很快就会发现, 这种单例的实现虽然
1. 线程安全.
2. 兼容 ARC.
3. 代码简洁.
但是, 无法控制调用者再次创建新的对象.
ToolManager *m1 = [ToolManager sharedToolManager];m1.tName = @"m1's tName";ToolManager *m2 = [[ToolManager alloc] init];m2.tName = @"m2's tName";NSLog(@"m1 = %@ | tName = %@", m1, m1.tName);NSLog(@"m2 = %@ | tName = %@", m2, m2.tName);很明显, m1 与 m2 不是同一个实例对象.
解决方案 1: 覆写 allocWithZone
+ (id)allocWithZone:(NSZone *)zone {NSString *reason = [NSString stringWithFormat:@"Attempt to allocate a second instance of the singleton %@", [self class]];NSException *exception = [NSException exceptionWithName:@"Multiple singletons" reason:reason userInfo:nil];[exception raise];return nil;}这样不管你是执行[ToolManager alloc] init]还是执行[ToolManager new]都会 crash, 并给出上面的提示信息.
修改 ToolManager.m 代码示例
#import "ToolManager.h"@implementation ToolManager+ (ToolManager *)sharedToolManager{static ToolManager *manager;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{manager = [[super allocWithZone:nil] init];});return manager;}- (instancetype)init{if (self = [super init]) {//初始化}return self;}+ (id)allocWithZone:(NSZone *)zone {NSString *reason = [NSString stringWithFormat:@"Attempt to allocate a second instance of the singleton %@", [self class]];NSException *exception = [NSException exceptionWithName:@"Multiple singletons" reason:reason userInfo:nil];[exception raise];return nil;}@end
解决方案 2: 使用 __attribute__ 并结合方案1.
ToolManager.h
#import <Foundation/Foundation.h>@interface ToolManager : NSObject@property (copy, nonatomic) NSString *tName;+ (ToolManager *)sharedToolManager;// clue for improper use (produces compile time error)+ (instancetype) alloc__attribute__((unavailable("alloc not available, call sharedToolManager instead")));- (instancetype) init__attribute__((unavailable("init not available, call sharedToolManager instead")));+ (instancetype) new__attribute__((unavailable("new not available, call sharedToolManager instead")));@endToolManager.m#import "ToolManager.h"@implementation ToolManager+ (ToolManager *)sharedToolManager{static ToolManager *manager;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{manager = [[super alloc] _initInstance];});return manager;}- (instancetype)init{if (self = [super init]) {//初始化}return self;}- (instancetype)_initInstance{return [super init];}+ (id)allocWithZone:(NSZone *)zone {NSString *reason = [NSString stringWithFormat:@"Attempt to allocate a second instance of the singleton %@", [self class]];NSException *exception = [NSException exceptionWithName:@"Multiple singletons" reason:reason userInfo:nil];[exception raise];return nil;}@end
这种方案相比第一种方案的好处, 就是在编译时就会提示错误信息.
如果不结合方案1的话, 调用者可以通过
[ToolManager allocWithZone:nil];来创建新的对象实例.
附: github 上写的
MySingleton.h
#import <Foundation/Foundation.h>@interface MySingleton : NSObject+(instancetype) sharedInstance;// clue for improper use (produces compile time error)+(instancetype) alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));-(instancetype) init __attribute__((unavailable("init not available, call sharedInstance instead")));+(instancetype) new __attribute__((unavailable("new not available, call sharedInstance instead")));@end
MySingleton.m#import "MySingleton.h"@implementation MySingleton+(instancetype) sharedInstance {static dispatch_once_t pred;static id shared = nil;dispatch_once(&pred, ^{shared = [[super alloc] initUniqueInstance];});return shared;}-(instancetype) initUniqueInstance {return [super init];}@end上面说过, 这种方式无法阻止调用者通过调用 allocWithZone 来创建实例.
推荐阅读
*Singletons in Objective-C
*Preventing other instances of your dispatch_once’d singleton
*使用dispatch_once实现单例模式
附上完整实现.
ToolManager.h
人生最大的错误是不断担心会犯错