OC坑集: ARC单例模式

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

人生最大的错误是不断担心会犯错

OC坑集: ARC单例模式

相关文章:

你感兴趣的文章:

标签云: