参考资料:
一、一个优雅的单例案例
1.1、简介
- 使用官方推荐的dispatch_once创建
- 避免循环引用,使用[super allocWithZone:NULL]创建
- 保持单列完整性,遵循了NSCopying,NSMutableCopying,重写了
- 标记不能被继承
__attribute__ ((objc_subclassing_restricted))
- 考虑了创建对象的几种方式,以及调用顺序
1.2、FengOnceClass.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
// 标记不能被继承
__attribute__ ((objc_subclassing_restricted))
@interface FengOnceClass : NSObject<NSCopying,NSMutableCopying>
+ (instancetype)sharedInstance;
@end
NS_ASSUME_NONNULL_END
1.3、FengOnceClass.m
#import "FengOnceClass.h"
@implementation FengOnceClass
+ (instancetype)sharedInstance {
static FengOnceClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];//避免循环引用,故用super
/*
* 也可以此处用self,alloc中调用super
* 但是不推荐,有代码冗余,推荐此处直接使用super
*/
//sharedInstance = [[self allocWithZone:NULL] init];
});
return sharedInstance;
}
- (instancetype)init {
if (self = [super init]) {
//初始化,需要有初始化的就写,没有可以省略这个方法
}
return self;
}
#pragma mark --重写 保证为真正的单列
+ (id) allocWithZone:(struct _NSZone *)zone {
/*
* 需要考虑开发着的调用顺序,故不能直接return self;
* 因为开发者可能先调用alloc再调用sharedInstance
*/
//return self;
return [self sharedInstance];
}
/*
* 有的文章说需要重写new
* 但验证后,是不需要重写的
* new调用的就是alloc和init,只要重写allocWithZone就好了
*/
//+ (instancetype)new {
// return [self sharedInstance];
//}
/*
* copy和mutableCopy其实不重写也可以,但是会崩溃unrecognized selector sent to instance XXXX"
* 因为需要支持copy和mutableCopy必须支持NSCopying,NSMutableCopying协议
* 但是为了代码的严谨性还是重写一下比较好
* 可以直接return self,是因为iOS的记住,如果是实例为空对象,就不会调到实例方法
*/
/*
* copy和mutableCopy其实不重写也可以,但是会崩溃unrecognized selector sent to instance XXXX"
* 因为需要支持copy和mutableCopy必须支持NSCopying,NSMutableCopying协议
* 但是为了代码的严谨性还是重写一下比较好
* 其实可以直接return self,因为iOS的特性,如果是实例为空对象,就不会调到实例方法
* 但是为了代码的严谨性还是是用sharedInstance
*/
- (id)copyWithZone:(nullable NSZone *)zone {
Class selfClass = [self class];
return [selfClass sharedInstance];
}
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
Class selfClass = [self class];
return [selfClass sharedInstance];
}
@end
二、为什么要标记成不可继承
- 现把FengOnceClass标记为不能被继承的标示屏蔽,然后定义一个FengOncePlusClass,什么都不做,不定义属性不定义方法,只是继承FengOnceClass,代码如下:
//.h文件
#import "FengOnceClass.h"
NS_ASSUME_NONNULL_BEGIN
@interface FengOncePlusClass : FengOnceClass
@end
NS_ASSUME_NONNULL_END
//.m文件
#import "FengOncePlusClass.h"
@implementation FengOncePlusClass
@end
- 使用如下:
FengOnceClass *test1 = [FengOnceClass sharedInstance];
test1.name = @"Feng";
FengOncePlusClass *test2 = [FengOncePlusClass sharedInstance];
test2.name = @"FengPlus";
NSLog(@"test1 %p test1.name %@ test2 %p test2.name %@",test1,test1.name,test2,test2.name);
- 打印结果如下:
2020-11-24 16:47:03.574846+0800 BlueToothDemo[51487:3514206] test1 0x600002b70030 test1.name FengPlus test2 0x600002b70030 test2.name FengPlus
- 分析:
地址都是一样的,然后name都变成了"FengPlus",这显然不是我们想要的单例,不能保持单列的完成性了。所以还是要开启不能被继承的标示
。但是如果业务有需求,需要实现可继承单例,又不影响单例,是否能实现了。答案是可以的,继续往下看。
3、可继承单列实现
-
在单例上做如下改动
- 屏蔽
不能被继承的标示
- 使用runTime的关联对象进行创建
- 屏蔽
-
FengOnceClass.h,代码如下:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface FengOnceClass : NSObject<NSCopying,NSMutableCopying>
+ (instancetype)sharedInstance;
@end
NS_ASSUME_NONNULL_END
- FengOnceClass.m,代码如下:
#import "FengOnceClass.h"
@implementation FengOnceClass
//使用runTime的关联对象进行创建
+(instancetype)sharedInstance {
Class selfClass = [self class];
// 从类中获取对象
id instance = objc_getAssociatedObject(selfClass, @"sharedInstance");
if (!instance) {
// 不存在,创建对象
instance = [[super allocWithZone:NULL] init];
// 给类绑定对象
objc_setAssociatedObject(selfClass, @"sharedInstance", instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return instance;
}
- (instancetype)init {
if (self = [super init]) {
//初始化,需要有初始化的就写,没有可以省略这个方法
}
return self;
}
+ (id) allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(nullable NSZone *)zone {
Class selfClass = [self class];
return [selfClass sharedInstance];
}
- (id)mutableCopyWithZone:(nullable NSZone *)zone {
Class selfClass = [self class];
return [selfClass sharedInstance];
}
@end
- 使用如下:
FengOnceClass *test1 = [FengOnceClass sharedInstance];
test1.name = @"Feng";
FengOncePlusClass *test2 = [FengOncePlusClass sharedInstance];
test2.name = @"FengPlus";
NSLog(@"test1 %p test1.name %@ test2 %p test2.name %@",test1,test1.name,test2,test2.name);
-
打印结果和分析如下:
可继承单例
网友评论