实现安全的单例
单例是个很有用的设计模式,降低了在复杂环境下的管理难度。但是如果想要合作开发,最好告诉合作者 你的哪个类可以使用单例。
默认情况下,即使你实现了单例,但是如果别人不知道,他可能第一想法还是自己手动创建一个对象来。 因此,我们最好可以在调用创建对象方法时提示一下使用者。
在clang编译器前端中有一个属性 __attribute__
,这个属性很强大,可以实现各种奇怪的特性。这里,我们
使用 __attribute__((unavailable("请使用单例")))
,如下例:
- (instancetype)init __attribute__((unavailable("请使用单例")));
加入上边例子中的特殊指令后,在Xcode中就不能智能提示出 init
方法了,如果手动打出,会提示错误:“请使用单例”。
同样还可以使用一种简略形式:
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
这种形式不会提示具体的错误消息,如“请使用单例”,但是在Xcode中可以自动提示打出,而 __attribute__
需要
手动打出。
前边的两种方法都是clang提供的指令,同样我们也可以使用覆盖方法调用的策略。
使用断言 在覆盖init的方法内部最前面加入断言:
NSAssert(NO, @"请使用单例");
抛出错误 在覆盖的方法内部最前面调用抛出错误的方法:
[self doesNotRecognizeSelector:_cmd];
我看到有些地方会加入防拷贝策略,就是重写copy相关方法,直接返回单例。但是实际上一般的单例对象,如果不自己实现NSCopying或NSMutableCopying协议,那么是无法调用copy和mutableCopy方法的,因此,防拷贝作用不大。而且最好不要覆盖init/new/copy/mutableCopy 等方法来返回单例,这种方法防止创建新的对象,但是我认为这样实际上使代码变得奇怪,因为alloc&init/new/mutableCopy默认都会返回一个 新的对象,如果直接覆盖,无疑会造成逻辑的冲突。
上面的几种方法里面,显然第一种使用 __attribute__
的方法最简单并且可以在程序崩溃之前就告诉使用者
需要使用单例。