创建单例必须注意的3个要点:
- 一是某个类只能有一个实例
- 二是它必须自行创建这个实例
三是它必须自行向整个系统提供这个实例
使用单例(Singleton)的2个优点:
1.实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例。
2.灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
————————-方法1(别用)————————
01 首先创建一个继承于NSObject的一个单例类,这里以 Instance.h为例。
具体的讲解都在代码注释中,不一一瞎比比了:
在Instance.h文件中
#import <Foundation/Foundation.h>
@interface Instance : NSObject
//声明一个单例类方法
+ (id)sharedInstance;
@end
02 在 Instance.m 文件中:
#import "Instance.h"
@implementation Instance
//定义静态全局变量
static Instance *instance = nil;
//单例类方法实现
+ (id)sharedInstance {
//判断是否为空,若为空,创建
if (instance == nil) {
//创建单例
instance = [[self alloc] init];
}
//返回实例
return instance;
}
@end
这样一个简单的单例就完成了,但是,为了说一下为何不推荐这个方法,我们接着在控制器 ViewController.m文件中演示一下。
03 在 ViewController.m 中
#import "ViewController.h"
#import "Instance.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Instance *instance1 = [Instance sharedInstance];
NSLog(@"单例1的地址为:%@", instance1);
Instance *instance2 = [Instance sharedInstance];
NSLog(@"单例2的地址为:%@", instance2);
Instance *instance3 = [[Instance alloc] init];
NSLog(@"单例3的地址为:%@", instance3);
}
@end
打印结果:
从打印结果可以发现:单例1和单例2的打印地址一样,而单例3的地址不同,原因是什么呢?很简单,第3中的单例对象获取的方式是重新创建了一个单例实例,也就是说系统又alloc了一块内存空间。而真正的获取一个单例实例我们通常用的是[Instance sharedInstance],并且单例只能有一个实例对象。当然,既然存在问题,那必然有解决的办法,所以为了防止用户有时获取单例不慎采用了alloc的方法,这里推荐相当好的一个单例创建方法,看下面。
———————-方法2(正确的单例创建写法)—————————–
04 同样,在Instance.h文件中
#import <Foundation/Foundation.h>
@interface Instance : NSObject
//声明一个单例类方法
+ (id)sharedInstance;
@end
05 在 Instance.m 文件中:
#import "Instance.h"
@implementation Instance
//定义静态全局变量
static Instance *instance = nil;
//单例的创建方法2
//多线程创建单例(推荐使用)
+ (id)sharedInstance {
//静态单例令牌,用作标识
//使用dispatch保证只创建一次,会自动加锁!如果使用其他方法创建的话,需要**自己加锁**!
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//创建单例,调用父类,并初始化
instance = [[super allocWithZone:nil] init];
});
//返回单例实例
return instance;
}
//防止外部调用alloc,复写系统自带的alloc方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
//返回当前类的单例
return [self sharedInstance];
}
@end
注:为了防止用户使用关键字copy导致单例不唯一,还需要重写:copyWithZone方法!!!
06 在 ViewController.m 中,同03,这里就不写了,重点看一下打印结果:
显然,尽管在控制器中的打印内容和方法1中的一样,但打印结果却是不同,这里打印的三种结果中的地址相同,也就是说获取到的是同一个单例实例对象。所以,为了防止用户使用 Instance *instance3 = [[Instance alloc] init]; 这样的alloc创建获取单例实例对象方法,推荐使用方法2多线程创建单例. 当然,网上还有很多创建单例的方法,大家可以尝试比较一下哪种更好。