iOS单例模式

2019-06-18  本文已影响0人  CrazyItCoder

单例模式大概是设计模式种较简单的一种设计模式。但在实际的开发过程中仍然存在一些坑。所以本文总结了下iOS中的单例模式。

什么是单例模式?

  • ensures a class only has one instance
  • provides a global point of access to it

苹果官方文档的一副图描述了请求普通类和单例的区别:


请求普通类与单例的区别

如何实现基本的单例模式?

Singleton *sharedInstance = nil;

+ (instancetype)sharedIntance {
    if (sharedInstance == nil) {
        sharedInstance = [[Singleton alloc] init];
    }
    
    return sharedInstance;
}

全局的变量sharedInstance有个缺点,可以被外部随意修改,为了隔离外部修改,可以设置成局部静态变量。

+ (instancetype)sharedInstance {
    static Singleton *sharedInstance = nil;
    if (sharedInstance == nil) {
        sharedInstance = [[Singleton alloc] init];
    }
    
    return sharedInstance;
}

单例的核心思想就算实现了。

多线程如何处理?

上述例子虽然实现了单例的核心思想,但依然存在问题。在多线程情况下即多个线程同时访问sharedInstance工厂方法,并不能保证只创建一个实例对象。

那么,如何保证在多线程的下依旧能够只创建一个实例对象呢?iOS下我们可以使用NSLock@synchronized等多种线程同步技术。

+ (instancetype)sharedInstance {
    static Singleton *sharedInstance = nil;
    @synchronized (self) {
        if (sharedInstance == nil) {
            sharedInstance = [[Singleton alloc] init];
        }
    }

    return sharedInstance;
}

@synchronized虽然保证了在多线程下调用sharedInstance工厂方法只会创建一个实例对象,但是@synchronized的性能较差。OC内部提供了一种更加高效的方式,那就是dispatch_once@synchronized性能相较于dispatch_once要差几倍,甚至几十倍。关于二者的性能对比,请参考这里

+ (instancetype)sharedInstance {
    static Singleton *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[Singleton alloc] init];
    });

    return sharedInstance;
}

Objective-C中实现单例存在的坑

上述实现单例的方式看起来很完美了。虽然我们提供了一个方便的工厂方法返回单例,但是用户依然能够调用alloc方法创建对象。这样外部使用的时候依旧能够创建多个实例。解决上述问题有两种方案:

1. 技术上实现无论怎么调用都返回同一个单例对象

To create a singleton as the sole allowable instance of a class in the current process. This code does the following:

  • It declares a static instance of your singleton object and initializes it to nil.
  • In your class factory method for the class (named something like “sharedInstance” or “sharedManager”), it generates an instance of the class but only if the static instance is nil.
  • It overrides the allocWithZone: method to ensure that another instance is not allocated if someone tries to allocate and initialize an instance of your class directly instead of using the class factory method. Instead, it just returns the shared object.
  • It implements the base protocol methods copyWithZone:, release, retain, retainCount, and autorelease to do the appropriate things to ensure singleton status. (The last four of these methods apply to memory-managed code, not to garbage-collected code.)
+ (instancetype)sharedInstance {
    static Singleton *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[super allocWithZone:NULL] init];
    });

    return sharedInstance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [self sharedInstance];
}

- (id)copy {
    return [Singleton sharedInstance];
}

- (id)mutableCopy {
    return [Singleton sharedInstance];
}

如下图所示,不管使用何种方式创建对象都返回相同的实例对象。


实例变量的地址

2.利用编译器特性给用户提示,但不强制约束

利用编译器特性直接告诉外部newalloccopymutableCopy方法不能直接调用,否则编译不通过。

+ (instancetype)sharedInstance;

+ (instancetype)new OBJC_UNAVAILABLE("use sharedInstance instead.");
+ (instancetype)alloc OBJC_UNAVAILABLE("use sharedInstance instead.");
- (id)copy OBJC_UNAVAILABLE("use sharedInstance instead.");
- (id)mutableCopy OBJC_UNAVAILABLE("use sharedInstance instead.");

若直接调用alloc等方法创建对象,编译器则会给出错误提示:

编译器错误提示

单例模式潜在的问题

参考文献:
Singleton
Creating a Singleton Instance

上一篇 下一篇

猜你喜欢

热点阅读