引用:《 Objective-C编程之道 iOS设计模式解析》
单例模式思想:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例类提供创建与访问类唯一对象的访问点,并保证它唯一、一致而且为人熟知。
何时使用:
1、类只能有一个实例,而且必须从一个为人熟知的访问点对其进行访问,如工厂方法;
2、这个唯一的实例只能通过子类化进行扩展,而且扩展的对象不会破坏客户端代码。
单例实现:
如何保证类只能创建一个实例?
1、确保发起调用的对象不能以其他分配方式实例化单例对象。否则就可能创建单例的多个实例。
2、对单例对象实例化的限制应该与引用计数内存模型共存。
案例一:
@interface Singleton : NSObject
+(instancetype)sharedInstance;
@end
@implementation Singleton
static Singleton *singleton_=nil;
+(instancetype)sharedInstance
{
if(singleton_==nil)
{
singleton_=[[Singleton alloc]init];
}
return singleton_;
}
@end
//测试:
Singleton *k= [Singleton sharedInstance];
NSLog(@"0==>%p",k);
Singleton *m=[[Singleton alloc]init];
NSLog(@"1==>%p",m);
//结果:多个实例对象
0==>0x6000030c8790
1==>0x6000030c3150
结果:可以创建多个单例类的实例,需要改进。
案例二:
@implementation Singleton
static Singleton *singleton_=nil;
+(instancetype)sharedInstance
{
if(singleton_==nil)
{
singleton_=[[super allocWithZone:NULL]init];
}
return singleton_;
}
//alloc方法使用设为NULL的zone来调用allocWithZone方法;在默认区域为实例分配内存。
+(id)allocWithZone:(struct _NSZone *)zone
{
return [self sharedInstance];
}
@end
//测试01
Singleton *k= [Singleton sharedInstance];
NSLog(@"3==>%p",k);
Singleton *m=[[Singleton alloc]init];
NSLog(@"4==>%p",m);
//结果:
2019-05-12 09:34:33.905676+0800 Block[3997:226208] 3==>0x600003680700
2019-05-12 09:34:33.905847+0800 Block[3997:226208] 4==>0x600003680700
//多线程测试02
dispatch_queue_t t=dispatch_queue_create("z", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(t, ^{
Singleton *k= [Singleton sharedInstance];
NSLog(@"0==>%p",k);
});
dispatch_async(t, ^{
Singleton *k= [Singleton sharedInstance];
NSLog(@"1==>%p",k);
});
//结果:
2019-05-12 09:33:01.517330+0800 Block[3974:224945] 0==>0x600000800b10
2019-05-12 09:33:01.517329+0800 Block[3974:224946] 1==>0x60000081f200
MRC 时:
重写copyWithZone
;防止对象被复制产生新的对象。
+(id)copyWithZone:(struct _NSZone *)zone
{
@synchronized (self) {
if(singleton_==nil)
{
singleton_=[[super allocWithZone:NULL]init];
}
}
return singleton_;
}
结果:多线程开发中还是可以创建多个单例类的实例,需要继续改进。
案例三:
添加@synchronized()
指令(或者NSLock
加锁):对一段代码的使用加锁,其他试图执行该段代码的线程都会被阻塞。
指令@synchronized()
需要一个参数。该参数可以使任何的Objective-C对象,包括self。这个对象就是互斥信号量。他能够让一个线程对一段代码进行保护,避免别的线程执行该段代码。针对程序中的不同的关键代码段,我们应该分别使用不同的信号量。只有在应用程序编程执行多线程之前就创建好所有需要的互斥信号量对象来避免线程间的竞争才是最安全的。
//对方法sharedInstance修改如下:
+(instancetype)sharedInstance
{
@synchronized (self) {
if(singleton_==nil)
{
singleton_=[[super allocWithZone:NULL]init];
}
}
return singleton_;
}
//测试:
dispatch_queue_t t=dispatch_queue_create("z", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(t, ^{
Singleton *k= [Singleton sharedInstance];
NSLog(@"0==>%p",k);
});
dispatch_async(t, ^{
Singleton *k= [Singleton sharedInstance];
NSLog(@"1==>%p",k);
});
//结果:
2019-05-12 10:09:57.195178+0800 Block[4385:259379] 0==>0x600003208780
2019-05-12 10:09:57.195178+0800 Block[4385:259380] 1==>0x600003208780
其他单例实现:
使用GCD实现单例:
在GCD中提供了一种信号机制,也可以解决资源抢占问题(和同步锁的机制并不一样)。GCD中信号量是dispatch_semaphore_t类型,支持信号通知和信号等待。每当发送一个信号通知,则信号量+1;每当发送一个等待信号时信号量-1,;如果信号量为0则信号会处于等待状态,直到信号量大于0开始执行。根据这个原理我们可以初始化一个信号量变量,默认信号量设置为1,每当有线程进入“加锁代码”之后就调用信号等待命令(此时信号量为0)开始等待,此时其他线程无法进入,执行完后发送信号通知(此时信号量为1),其他线程开始进入执行,如此一来就达到了线程同步目的。
参考:Objective-C的锁机制 :https://blog.csdn.net/roger_jin/article/details/45307951
+(instancetype)sharedInstance
{
if(singleton_==nil){
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton_=[[super allocWithZone:NULL]init];
});
}
return singleton_;
}
Cocoa Touch 框架中单例模式使用
一、UIApplication类
创建&访问: 由UIApplicationMain
函数在应用启动时创建为单例对象。之后,对同一UIApplication实例可以通过shareApplication类方法进行访问。
职责: 它为应用程序处理许多内务管理任务,包括传入的用户事件的最初路由,以及为UIControl分发动作消息给合适的目标对象。还维护应用程序中打开的所有UIWindow对象的列表。应用程序总是被分配一个UIApplicationDelegate对象( @interface AppDelegate : UIResponder <UIApplicationDelegate>
)应用程序把重要的运行时事件通知给它,比如iOS应用启动、内存不足警告、应用终止和后台执行等。让委托(delegate)有机会做出适当响应。
二、NSFileManager 类
其单例实现不是线程安全的,推荐生成新的NSFileManager实例保证线程安全。