所谓单例就是确保在程序运行过程中只创建一个对象实例,可以用于需要被多次广泛或者说多次使用的资源中,比如我们常见的网络请求类、工具类、以及其他管理类。在iOS开发中,单例模式是非常有用的设计模式
一.iOS系统单例
-
NSUserDefaults
类和defaultUser
方法 -
UIApplication
类和sharedApplication
方法,我们一般使用该方法来创建全局变量 -
NSBundle
类和mainBundle
方法 -
NSFileManager
类和defaultManager
方法 -
NSNotificationCenter
类和DefaultManager
方法,其中NSNotificationCenter
也实现了观察者模式
二. 使用单例模式的作用
它可以保证某个类在程序运行过程中最多只有一个实例,也就是对象实例只占用一份内存资源
三. 单例模式的三个要点
- 该类有且只有一个实例
- 该类必须能够自行创建这个实例
- 该类必须能够自行向整个系统提供这个实例
四. 为什么要使用单例
- 节省内存开销。如果某个对象需要被多个其它对象使用,那可以考虑使用单例,因为这样该类只使用一份内存资源
- 使用单例,可以确保其它类只获取类的一份数据(变量值)
五. 单例模式优缺点
- 优点
- 提供了对唯一实例的受控访问
- 由于在系统内存中只存在一个对象,因此可以节省系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能
- 因为单例模式的类控制了实例化的过程,所以类可以更加灵活修改实例化过程
- 缺点
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难
- 单例类的职责过重,在一定程度上违背了"单一职责原则"
六.单例的实现
- 基本步骤
1. 为单例对象创建一个静态实例,可以写成全局的,也可以在类方法里面实现,并初始化为nil
2. 实现一个实例构造方法,检查上面声明的静态实例是否为nil,如果是,则创建并返回一个本类的实例
3. 重写allocWithZone方法,用来保证其他人直接使用alloc和init试图获得一个新实例的时候不产生一个新实例
4. 适当实现copyWithZone,mutableCopyWithZone,非ARC下还需要实现release和autorelease方法
- 代码示例
新建一个Singleton
类,在Singleton.h
中实现如下
//
// Singleton.h
// TestSingleton
//
// Created by taobaichi on 2017/4/13.
// Copyright © 2017年 MaChao. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
+(Singleton *)sharedInstance;
@end
在Singleton.m
添加如下代码
//
// Singleton.m
// TestSingleton
//
// Created by taobaichi on 2017/4/13.
// Copyright © 2017年 MaChao. All rights reserved.
//
#import "Singleton.h"
static Singleton * sharedSingleton = nil;
@implementation Singleton
+(Singleton *) sharedInstance
{
if (sharedSingleton == nil) {
sharedSingleton = [[Singleton alloc]init];
}
return sharedSingleton;
}
@end
这样就创建一个简单的单例模式,实际上有一部分程序员也是这样实现的,但实际上这是一个不严格
的版本,也就是一个假单例
,在实际中使用,如果我们使用alloc
,copy
等方法创建对象时,依然会创建新的实例,而且如果多线程同时访问时候也会创建多个实例,因此这样做是非线程安全
的。
下面对Singleton.m
进行改进
//
// Singleton.m
// TestSingleton
//
// Created by taobaichi on 2017/4/13.
// Copyright © 2017年 MaChao. All rights reserved.
//
#import "Singleton.h"
static Singleton * _sharedSingleton = nil;
@implementation Singleton
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedSingleton = [super allocWithZone:zone];
});
return _sharedSingleton;
}
-(id)init
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedSingleton = [super init];
});
return _sharedSingleton;
}
+(instancetype)sharedInstance
{
return [[self alloc]init];
}
+(id)copyWithZone:(struct _NSZone *)zone
{
return _sharedSingleton;
}
+(id)mutableCopyWithZone:(struct _NSZone *)zone
{
return _sharedSingleton;
}
@end
- 测试
一. 对假单例进行调用
单例写法
// Singleton.m
// TestSingleton
//
// Created by taobaichi on 2017/4/13.
// Copyright © 2017年 MaChao. All rights reserved.
//
#import "Singleton.h"
static Singleton * _sharedSingleton = nil;
@implementation Singleton
+(Singleton *)sharedInstance
{
if (_sharedSingleton == nil) {
_sharedSingleton = [[Singleton alloc]init];
}
return _sharedSingleton;
}
@end
- 调用方法一
//
// ViewController.m
// TestSingleton
//
// Created by taobaichi on 2017/4/13.
// Copyright © 2017年 MaChao. All rights reserved.
//
#import "ViewController.h"
#import "Singleton.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self testSingleton];
}
-(void)testSingleton
{
Singleton * single1 = [Singleton sharedInstance];
Singleton * single2 = [Singleton sharedInstance];
if (single1 == single2) {
NSLog(@"single1 == single2");
}
NSLog(@"single1地址 %@",single1);
NSLog(@"single2地址 %@",single2);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果
2017-04-13 13:12:27.327 TestSingleton[2515:106202] single1 == single2
2017-04-13 13:12:27.327 TestSingleton[2515:106202] single1地址 <Singleton: 0x6000000147f0>
2017-04-13 13:12:27.327 TestSingleton[2515:106202] single2地址 <Singleton: 0x6000000147f0>
- 调用方法二:
//
// ViewController.m
// TestSingleton
//
// Created by taobaichi on 2017/4/13.
// Copyright © 2017年 MaChao. All rights reserved.
//
#import "ViewController.h"
#import "Singleton.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self testSingleton];
}
-(void)testSingleton
{
Singleton * single1 = [[Singleton alloc]init];
Singleton * single2 = [[Singleton alloc]init];
if (single1 == single2) {
NSLog(@"single1 == single2");
}
NSLog(@"single1地址 %@",single1);
NSLog(@"single2地址 %@",single2);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果:
2017-04-13 13:13:47.526 TestSingleton[2529:108124] single1地址 <Singleton: 0x60800001cda0>
2017-04-13 13:13:47.526 TestSingleton[2529:108124] single2地址 <Singleton: 0x60800001cdb0>
可以看到,假单例当用alloc实例化的时候,生成的并不是一个单例,也就是说并不是同一个对象
二. 对真单例无论怎么实例化生成的都是同一个对象
真单例写法
//
// Singleton.m
// TestSingleton
//
// Created by taobaichi on 2017/4/13.
// Copyright © 2017年 MaChao. All rights reserved.
//
#import "Singleton.h"
static Singleton * _sharedSingleton = nil;
@implementation Singleton
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedSingleton = [super allocWithZone:zone];
});
return _sharedSingleton;
}
-(id)init
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedSingleton = [super init];
});
return _sharedSingleton;
}
+(instancetype)sharedInstance
{
return [[self alloc]init];
}
+(id)copyWithZone:(struct _NSZone *)zone
{
return _sharedSingleton;
}
+(id)mutableCopyWithZone:(struct _NSZone *)zone
{
return _sharedSingleton;
}
@end
调用
//
// ViewController.m
// TestSingleton
//
// Created by taobaichi on 2017/4/13.
// Copyright © 2017年 MaChao. All rights reserved.
//
#import "ViewController.h"
#import "Singleton.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self testSingleton];
}
-(void)testSingleton
{
Singleton * single1 = [[Singleton alloc]init];
Singleton * single2 = [[Singleton alloc]init];
if (single1 == single2) {
NSLog(@"single1 == single2");
}
NSLog(@"single1地址 %@",single1);
NSLog(@"single2地址 %@",single2);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印结果
2017-04-13 13:20:31.480 TestSingleton[2562:111462] single1 == single2
2017-04-13 13:20:31.481 TestSingleton[2562:111462] single1地址 <Singleton: 0x608000012b00>
2017-04-13 13:20:31.481 TestSingleton[2562:111462] single2地址 <Singleton: 0x608000012b00>