Through the Chaos!

==================> 知我罪我,唯有春秋!

实现安全的单例

单例是个很有用的设计模式,降低了在复杂环境下的管理难度。但是如果想要合作开发,最好告诉合作者 你的哪个类可以使用单例。

默认情况下,即使你实现了单例,但是如果别人不知道,他可能第一想法还是自己手动创建一个对象来。 因此,我们最好可以在调用创建对象方法时提示一下使用者。

在clang编译器前端中有一个属性 __attribute__ ,这个属性很强大,可以实现各种奇怪的特性。这里,我们 使用 __attribute__((unavailable("请使用单例"))) ,如下例:

- (instancetype)init __attribute__((unavailable("请使用单例")));

加入上边例子中的特殊指令后,在Xcode中就不能智能提示出 init 方法了,如果手动打出,会提示错误:“请使用单例”。

同样还可以使用一种简略形式:

- (instancetype)init UNAVAILABLE_ATTRIBUTE;

这种形式不会提示具体的错误消息,如“请使用单例”,但是在Xcode中可以自动提示打出,而 __attribute__ 需要 手动打出。

前边的两种方法都是clang提供的指令,同样我们也可以使用覆盖方法调用的策略。

  1. 使用断言 在覆盖init的方法内部最前面加入断言:

    NSAssert(NO, @"请使用单例");
    
  2. 抛出错误 在覆盖的方法内部最前面调用抛出错误的方法:

    [self doesNotRecognizeSelector:_cmd];
    

我看到有些地方会加入防拷贝策略,就是重写copy相关方法,直接返回单例。但是实际上一般的单例对象,如果不自己实现NSCopying或NSMutableCopying协议,那么是无法调用copy和mutableCopy方法的,因此,防拷贝作用不大。而且最好不要覆盖init/new/copy/mutableCopy 等方法来返回单例,这种方法防止创建新的对象,但是我认为这样实际上使代码变得奇怪,因为alloc&init/new/mutableCopy默认都会返回一个 新的对象,如果直接覆盖,无疑会造成逻辑的冲突。

上面的几种方法里面,显然第一种使用 __attribute__ 的方法最简单并且可以在程序崩溃之前就告诉使用者 需要使用单例。

Comments

comments powered by Disqus