注册

iOS之手写单例

一、 不严谨写法

先附上不严谨的创建单例的写法

  • SignalModel.h
@interface SignalModel : NSObject
+ (instancetype)shareInstance;
@end
  • SignalModel.m
@implementation SignalModel

+ (instancetype)shareInstance {
static SignalModel *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[SignalModel alloc] init];
});
return _instance;
}

外界使用

SignalModel *signal1 = [[SignalModel alloc] init];
SignalModel *signal2 = [SignalModel shareInstance];
SignalModel *signal3 = [SignalModel shareInstance];
NSLog(@"\nsignal1 = %@\nsignal2 = %@\nsignal3 = %@\n",signal1,signal2,signal3);

打印结果

e904198ca5f48182a0e556a7a4e6fa1c.png

1. 通过上面的测试,可以看到通过shareInstance方法获取的对象是相同的,但是用alloc和init构造对象时,得到的对象却是不一样的。

2. 通过不同的方式获得不同的对象,是有问题的,所以要封锁初始化的方式,如alloc,copy,mutableCopy,new

摘抄的原理哈
创建对象的步骤分为申请内存(alloc)、初始化(init)这两个步骤,我们要确保对象的唯一性,因此在第一步这个阶段我们就要拦截它。当我们调用alloc方法时,OC内部会调用allocWithZone这个方法来申请内存,我们覆写这个方法,然后在这个方法中调用shareInstance方法返回单例对象,这样就可以达到我们的目的。拷贝对象也是同样的原理,覆写copyWithZone方法,然后在这个方法中调用shareInstance方法返回单例对象

二、正确写法

  • SignalModel.m
// 实现copy协议
@interface SignalModel()<NSCopying, NSMutableCopying>

@end

+ (instancetype)shareInstance {
static SignalModel *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[super allocWithZone:NULL] init];
});
return _instance;
}

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

- (id)copyWithZone:(NSZone *)zone {
return self;
}

- (id)mutableCopyWithZone:(NSZone *)zone {
return self;
}

1. shareInstance单例方法中,变量的初始化改成[[super allocWithZone:NULL] init]

2. 实现copyWithZone:和mutableCopyWithZone:方法

测试代码

SignalModel *signal1 = [[SignalModel alloc] init];
SignalModel *signal2 = [SignalModel shareInstance];
SignalModel *signal3 = [SignalModel shareInstance];
SignalModel *signal4 = [SignalModel new];
SignalModel *signal5 = [signal1 copy];
SignalModel *signal6 = [signal2 mutableCopy];

NSLog(@"\nsignal1 = %@\nsignal2 = %@\nsignal3 = %@\nsignal4 = %@\nsignal5 = %@\nsignal6 = %@",signal1,signal2,signal3,signal4,signal5,signal6);

打印结果

78cc97bea1fe25660cfea01691a18f8b.png

无论通过哪种方式创建出来的实例对象,其内存地址都是一样的,所以该种写法才是严谨的。

转自:https://juejin.cn/post/6844903806027694087


0 个评论

要回复文章请先登录注册