美文网首页iOS-Objective-C
OC中单例的实现原理

OC中单例的实现原理

作者: 追沐 | 来源:发表于2017-09-22 01:18 被阅读21次

我们知道单例是在整个工程当中只有一个该类实例,怎么才能保证每次都只返回一个实例而不是另外一个实例呢?

  • 单例是一个对象,也是要被创建和初始化的,只是为了实现全局就创建一次,我们做了一些处理,整个工程当中只有一个该类实例。

一、最常见写单例的方式

这里创建了一个类TestClass,实现了一下大家平时写的单例
.h

#import <Foundation/Foundation.h>

@interface SingleTestClass : NSObject

+ (instancetype)sharedInstance;

@end

.m

#import "SingleTestClass.h"

@implementation SingleTestClass

+ (instancetype)sharedInstance {
    static SingleTestClass *single = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        single = [[self alloc] init];
    });
    return single;
}
@end

ok完事了,一个单例就这样产生了?用了onceToken,在其里面alloc init,那整个项目中就只有这一个实例了吧。其实不然。

  • 这样写能保证我们每次调用sharedInstance的时候返回的是同一个实例,也就是我们在onceToken里面创建的实例。dispatch_once(&onceToken, ^{
    single = [[self alloc] init];
    });方法保证只执行一遍

之所以这样能只返回一个实例的前提是,每次使用该类的对象的时候只通过提供的这个-(id)sharedInstance方法,才能保证每次都只返回一个实例而不是另外一个实例。

换句话说我们如果使用了alloc init来创建该类的对象,那肯定返回的就是另一个实例了。当然你可能会想,为什么要alloc init呢,用sharedInstance不就行了,我反问一句,为什么不能alloc init呢,难道不是OC对象?

  • 上述写法是不能完全保证每次都只返回一个实例而不是另外一个实例

二、sharedInstance方法的局限性

我们这样来看一下就知道了

SingleTestClass *testClass1 = [[SingleTestClass alloc] init];
SingleTestClass *testClass2 = [[SingleTestClass alloc] init];
SingleTestClass *testClass3 = [[SingleTestClass alloc] init];

SingleTestClass *shardeClass1 = [SingleTestClass sharedInstance];
SingleTestClass *shardeClass2 = [SingleTestClass sharedInstance];
SingleTestClass *shardeClass3 = [SingleTestClass sharedInstance];

NSLog(@"%@ \n %@ \n %@ \n %@ \n %@ \n %@ ",testClass1,testClass2,testClass3,shardeClass1,shardeClass2,shardeClass3);

我们以不同的形式创建了6个对象,打印了地址,结果显示如下:

<SingleTestClass: 0x60800001cf10> 
 <SingleTestClass: 0x60800001cf20> 
 <SingleTestClass: 0x60800001cf30> 
 <SingleTestClass: 0x60800001cef0> 
 <SingleTestClass: 0x60800001cef0> 
 <SingleTestClass: 0x60800001cef0> 

用sharedInstance每次返回的是同一个实例,但是用alloc init每次会返回新的实例。

也就是说每次alloc init都会分配新的内存空间(关于内存空间这点看这),而每次sharedInstance则不会。

可以说这样写单例是不能完全保证安全的,那么怎么才能安全呢?

三、复写allocWithZone方法

通过重载父类创建对象时候分配内存地址的方法,我们让每次创建对象都只返回同一个内存空间,这样就能实现更加安全的单例模式。

关于allocWithZone方法,看这里

重写allocWithZone:

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static SingleTestClass *singleClass = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ 
        singleClass = [super allocWithZone:zone];
    });
    return singleClass;
}
@end

重载这个初始化方法,使得每次不管什么方式,创建该类的对象的时候都返回一个实例,也就是一个单例。实质上每次alloc的时候都将指正指向同一个内存空间。

为了方便外部调用我们还向外提供了一个类方法sharedInstance,返回该类的一个实例。

@implementation SingleTestClass

+ (instancetype)sharedInstance {
    return [[self alloc] init];
}

- (instancetype)init {
    if (self = [super init]) {
        
    }
    return self;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static SingleTestClass *singleClass = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleClass = [super allocWithZone:zone];//最先执行,只执行了一次
    });
    return singleClass;
}
@end

下面来验证一下:

SingleTestClass *testClass1 = [[SingleTestClass alloc] init];
SingleTestClass *testClass2 = [[SingleTestClass alloc] init];
SingleTestClass *testClass3 = [[SingleTestClass alloc] init];
SingleTestClass *shardeClass1 = [SingleTestClass sharedInstance];
SingleTestClass *shardeClass2 = [SingleTestClass sharedInstance];
SingleTestClass *shardeClass3 = [SingleTestClass sharedInstance];
NSLog(@"%@ \n %@ \n %@ \n %@ \n %@ \n %@ ",testClass1,testClass2,testClass3,shardeClass1,shardeClass2,shardeClass3);

打印结果:

<SingleTestClass: 0x610000019710> 
 <SingleTestClass: 0x610000019710> 
 <SingleTestClass: 0x610000019710> 
 <SingleTestClass: 0x610000019710> 
 <SingleTestClass: 0x610000019710> 
 <SingleTestClass: 0x610000019710>

的确是返回来了同一个实例,不管用alloc init创建还是直接用sharedInstance这个方法。

最后

这时候你应该想起来了,为什么面试官当初问你,这样写单例有什么有缺点,或者单例怎么写才最安全。

建议两篇结合看:
第一篇
第二篇即本篇。

相关文章

  • OC中单例的实现原理

    我们知道单例是在整个工程当中只有一个该类实例,怎么才能保证每次都只返回一个实例而不是另外一个实例呢? 单例是一个对...

  • 单例模式的书写

    ARC OC 中的单例 根据OC单例 改写成 Swift 中的单例 OC调用swift,需要#import "单例...

  • swiftly单例

    swiftly的单例比OC的更加简单! Oc中的单例:

  • Swift 单例

    OC中单例的实现: Swift 单例的实现: 注意: 1.Apple 文档指出:全局变量和结构体/枚举体的静态成员...

  • swift语法-14单例

    swift语法-14单例 OC中单例 Swift中单例 简写 Swift中最长用的方法

  • iOS Swift中如何创建单例

    单例就是该类在系统中只有一个实例,对比一下OC的单例创建方式来看一下Swift的单例是如何写的。 OC中的单例是这...

  • OC和Swift单例的写法

    一 、OC中单例的写法:1.普通单例的写法 2. 利用多线程来写单例 #pragma mark --- 普通单例写...

  • Swift严格的单例写法

    相比OC,Swift有很优雅的实现单例的写法。 实现 单例类Tools 客户端调用: 说明 当尝试使用 这种方法去...

  • iOS单例模式

    单例模式 解决“应用中只有一个单例”的一类问题。 Objecttive-C实现原理 单例模式一般会封装一个静态属性...

  • 单例

    内存中只有一个对象实例 提供一个全局访问点 OC中的单例 swift中的单例 swift改进过的单例

网友评论

    本文标题:OC中单例的实现原理

    本文链接:https://www.haomeiwen.com/subject/pcrxextx.html