几年前,笔者曾对 iOS 单例模式作过一番阐述,包括其优点以及 Apple 自身对单例模式的实现示例,详情可参考:iOS 单例模式详解。这里再做一个简短的总结,
单例的用处:主要用在封装网络请求,播放器,存放常用数据等。
单例的特点:只初始化一次,生命周期和程序的生命周期相同,访问方便。
下面一步一步以循序渐进的方式来将单例模式完善化。为了多线程安全,用 GCD 创建单例更加方便,由于 dispatch_once
只执行一次,在外面调用 sharedInstance
这个方法,返回的对象始终是一个。
static InstanceManager *_instance = nil;
@implementation InstanceManager
+ (instancetype)sharedInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
@end
在创建对象时,不小心调用了 alloc
或者 new
,那这就不是单例了。我们知道,在对象创建的时候,alloc
和 new
都会调用到 allocWithZone
方法,此时只需要重写 allocWithZone
方法即可。
+ (id)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
此时,如果不慎调用了 copy
或 mutableCopy
方法就会导致程序运行崩溃,自定义的类,要实现 copy
就要默认实现 NSCopying
协议,同理实现 mutablecopy
就要实现 NSMutableCopying
协议。NSCopying
协议中有一个唯一的方法- (id)copyWithZone:(nullable NSZone *)zone;
需要实现,同样的,NSMutableCopying
也有唯一一个方法- (id)mutableCopyWithZone:(nullable NSZone *)zone;
需要实现。
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
return _instance;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
return _instance;
}
最终的 InstanceManager.m 完整代码如下:
#import "InstanceManager.h"
@interface InstanceManager() <NSCopying,NSMutableCopying>
@end
static InstanceManager *_instance = nil;
@implementation InstanceManager
+ (instancetype)sharedInstance {
return [[self alloc] init];
}
+ (id)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_instance) {
_instance = [super allocWithZone:zone];
}
});
return _instance;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
return _instance;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
return _instance;
}
@end
到此,一个完整的 iOS 单例就创建完成了!
上文中出现了 copy 与 mutableCopy 属性,也就自然涉及到了 Objective-C 中浅拷贝与深拷贝的知识,由于本文的核心内容是单例模式,这里只对 copy 与 mutableCopy 相关要点作简要描述。
NSString *string = @"huangfei";
NSString *stringCopy = [string copy];
NSMutableString * stringMCopy = [string mutableCopy];
[stringMCopy appendString:@"Hero"];
运行上述代码查看内存地址可知,string 和 stringCopy 指向的是同一块内存区域,而系统则为 stringMCopy 分配了一个新的内存地址。由此可见,copy
是指针复制(浅拷贝),mutableCopy
是对象复制(深拷贝)。
值得注意的是,在 iOS 中并不是所有的对象都支持copy
,mutableCopy
,遵守NSCopying
协议的类可以发送copy
消息,遵守NSMutableCopying
协议的类才可以发送mutableCopy
消息。否则,会发生异常。