面试题引发的思考:
Q: 介绍单例及其用途?
- 单例模式保证系统中 一个类 只有 一个实例 而且该实例 易于外界访问。
- 只初始化一次,生命周期 和 程序的生命周期 相同,访问方便;
- 主要用来封装网络请求、播放器、存放常用数据等。
Q: 单例正确写法?
- OC正确写法如下:
// TODO: ----------------- Singleton类 -----------------
@interface Singleton : NSObject <NSCopying, NSMutableCopying>
+ (instancetype)sharedInstance;
- (void)justdoit;
@end
@implementation Singleton
// 初始化方法dispatch_once,本身是线程安全的,保证整个程序中只会执行一次
+ (instancetype)sharedInstance {
static Singleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[super allocWithZone:nil] init];
});
return instance;
}
// 重写 allocWithZone
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [Singleton sharedInstance];
}
// 遵从NSCopying协议,可通过copy方式创建对象
- (id)copyWithZone:(NSZone *)zone {
return self;
}
// 遵从NSMutableCopying协议,可通过mutableCopy方式创建对象
- (id)mutableCopyWithZone:(NSZone *)zone {
return self;
}
// 实例方法
- (void)justdoit {
NSLog(@"just do it");
}
@end
- Swift正确写法如下:
// TODO: ----------------- Singleton类 -----------------
class Singleton {
static let shared = Singleton()
// 私有化构造器
private init() { }
// 方法
func justdoit() {
print("just do it")
}
}
1. 单例
(1) 单例模式
单例模式保证系统中 一个类 只有 一个实例 而且该实例 易于外界访问。
- 只初始化一次,生命周期 和 程序的生命周期 相同,访问方便;
- 主要用来封装网络请求、播放器、存放常用数据等。
经常见到的一些系统单例:
[UIApplication sharedApplication]
:应用程序
[NSNotificationCenter defaultCenter]
:通知
[NSUserDefaults standardUserDefaults]
:本地化存储
[NSFileManager defaultManager]
:文件操作
(2) 单例写法
苹果官方推荐写法:
// TODO: ----------------- Singleton类 -----------------
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
- (void)justdoit;
@end
@implementation Singleton
+ (instancetype)sharedInstance {
// 声明一个 空的静态的 单例对象
static Singleton *instance = nil;
// 声明一个 静态的 gcd的单次任务
static dispatch_once_t onceToken;
// 执行gcd单次任务
dispatch_once(&onceToken, ^{
// 对对象进行初始化
instance = [[Singleton alloc] init];
});
return instance;
}
- (void)justdoit {
NSLog(@"just do it");
}
@end
通过[Singleton sharedInstance]
创建对象,即可调用相关方法:
// TODO: ----------------- ViewController类 -----------------
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/// 创建单例
Singleton *singleton = [Singleton sharedInstance];
[singleton justdoit];
}
@end
// 打印结果
Demo[1234:567890] just do it
(3) 以上写法问题
如果不通过[Singleton sharedInstance]
创建对象,而是通过alloc
或者new
创建对象,会出现什么问题呢?
// TODO: ----------------- ViewController类 -----------------
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/// 通过不同方式创建对象
Singleton *singletonA= [Singleton sharedInstance];
Singleton *singletonB= [Singleton sharedInstance];
Singleton *singletonC = [[Singleton alloc] init];
Singleton *singletonD = [Singleton new];
NSLog(@"singletonA - %@", singletonA);
NSLog(@"singletonB - %@", singletonB);
NSLog(@"singletonC - %@", singletonC);
NSLog(@"singletonD - %@", singletonD);
}
@end
// 打印结果
Demo[1234:567890] singletonA - <Singleton: 0x283d35290>
Demo[1234:567890] singletonB - <Singleton: 0x283d35290>
Demo[1234:567890] singletonC - <Singleton: 0x283d352a0>
Demo[1234:567890] singletonD - <Singleton: 0x283d352b0>
由以上结果可知:
实例对象singletonA
和singletonB
的地址值是相同的;而此两者与实例对象singletonC
和singletonD
的地址值是不同的。
说明此情况下:
通过[Singleton sharedInstance]
只会创建一次对象;而通过alloc
或者new
会创建新的对象。
因为单例模式保证系统中一个类只有一个实例,所以以上单例的写法不够严谨,会出现问题。
2. 单例的正确写法
(1) 正确写法一
在创建对象的时候,alloc
或者new
都会调用到allocWithZone:
方法,需要重写 allocWithZone:
方法。
如果调用了copy
或mutableCopy
方法就会导致程序运行崩溃,需要实现copy
或mutableCopy
就要遵循协议实现方法。
// TODO: ----------------- Singleton类 -----------------
@interface Singleton : NSObject <NSCopying, NSMutableCopying>
+ (instancetype)sharedInstance;
- (void)justdoit;
@end
@implementation Singleton
// 初始化方法dispatch_once,本身是线程安全的,保证整个程序中只会执行一次
+ (instancetype)sharedInstance {
static Singleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[super allocWithZone:nil] init];
});
return instance;
}
// 重写 allocWithZone
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [Singleton sharedInstance];
}
// 遵从NSCopying协议,可通过copy方式创建对象
- (id)copyWithZone:(NSZone *)zone {
return self;
}
// 遵从NSMutableCopying协议,可通过mutableCopy方式创建对象
- (id)mutableCopyWithZone:(NSZone *)zone {
return self;
}
// 实例方法
- (void)justdoit {
NSLog(@"just do it");
}
@end
通过各种方式创建对象,查看打印结果:
// TODO: ----------------- ViewController类 -----------------
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Singleton *singletonA= [Singleton sharedInstance];
Singleton *singletonB= [Singleton sharedInstance];
Singleton *singletonC = [[Singleton alloc] init];
Singleton *singletonD = [Singleton new];
Singleton *singletonE = [singletonA copy];
Singleton *singletonF = [singletonC mutableCopy];
NSLog(@"singletonA - %@", singletonA);
NSLog(@"singletonB - %@", singletonB);
NSLog(@"singletonC - %@", singletonC);
NSLog(@"singletonD - %@", singletonD);
NSLog(@"singletonE - %@", singletonE);
NSLog(@"singletonF - %@", singletonF);
}
@end
// 打印结果
Demo[1234:567890] singletonA - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonB - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonC - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonD - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonE - <Singleton: 0x2815b9310>
Demo[1234:567890] singletonF - <Singleton: 0x2815b9310>
由打印结果可知:
实例对象singletonA
~singletonF
的地址值是相同的。
说明此情况下:
通过各种方式创建对象,而对象只会创建一次,符合单例模式定义。
(2) 正确写法二
设置只能通过[Singleton sharedInstance]
创建对象,直接禁用alloc
、new
、copy
、mutableCopy
等方法即可:
// TODO: ----------------- Singleton类 -----------------
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
- (void)justdoit;
+ (instancetype)alloc NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)copy NS_UNAVAILABLE;
- (instancetype)mutableCopy NS_UNAVAILABLE;
@end
@implementation Singleton
+ (instancetype)sharedInstance {
static Singleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[Singleton alloc] init];
});
return instance;
}
- (void)justdoit {
NSLog(@"just do it");
}
@end
此时无法通过alloc
、new
、copy
、mutableCopy
等方法创建对象,调用会报错:
(3) Swift单例正确写法
Swift中创建单例很方便:
// TODO: ----------------- Singleton类 -----------------
class Singleton {
static let shared = Singleton()
// 私有化构造器
private init() { }
func justdoit() {
print("just do it")
}
}
// TODO: ----------------- ViewController类 -----------------
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any setup after loading the view.
let singleton = Singleton.shared
singleton.justdoit()
}
}