无论是爱还是恨,你都需要单例。实际上每个iOS或Mac OS应用都至少会有
UIApplication或
NSApplication.
什么是单例呢?Wikipedia是如此定义的:
在软件工程中,单例是一种用于实现单例的数学概念,即将类的实例化限制成仅一个对象的设计模式。
或者我的理解是:
单例是一种类,该类只能实例化一个对象。
尽管这是单例的实际定义,但在Foundation框架中不一定是这样。比如
NSFileManger和
NSNotificationCenter,分别通过它们的类方法
defaultManager和
defaultCenter获取。尽管不是严格意义的单例,这些类方法返回一个可以在应用的所有代码中访问到的类的共享实例。在本文中我们也会采用该方法。
使用Objective-C实现单例模式的最佳方式向来有很多争论,开发者(包括Apple在内)似乎每几年就会改变他们的想法。当Apple引入了
Grand Central Dispatch (GCD)(Mac OS 10.6和iOS4.0),他们也引入了一个很适合用于实现单例模式的函数。
该函数就是
dispatch_once:
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
该函数接收一个
dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整型,实际上作为
BOOL使用)。它还接收一个希望在应用的生命周期内仅被调度一次的代码块,对于本例就用于shared实例的实例化。
dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如
@synchronized之类的来防止使用多个线程或者队列时不同步的问题。
Apple的
GCD Documentation证实了这一点:
如果被多个线程调用,该函数会同步等等直至代码块完成。
实际要如何使用这些呢?
好吧,假设有一个
AccountManager类,你想在整个应用中访问该类的共享实例。你可以按如下代码简单实现一个类方法:
+ (AccountManager *)sharedManager {
static AccountManager *sharedAccountManagerInstance = nil;
static dispatch_once_t predicate; dispatch_once(&predicate, ^{
sharedAccountManagerInstance = [[self alloc] init];
});
return sharedAccountManagerInstance;
}
这就意味着你任何时候访问共享实例,需要做的仅是:
AccountManager *accountManager = [AccountManager sharedManager];
就这些,你现在在应用中就有一个共享的实例,该实例只会被创建一次。
该方法有很多优势:
1 线程安全
2 很好满足静态分析器要求
3 和自动引用计数(ARC)兼容
4 仅需要少量代码
该方法的劣势就是它允许创建一个非共享的实例(其实无妨):
AccountManager *accountManager = [[AccountManager alloc] init];
有些时候你希望有这种行为,但如果正在想要的是仅一个实例被实例化就需要注意这点。
宏定义:
为了简化使用ARC+GCD来创建单例,可以定义下面这样的一个宏:
1.#define DEFINE_SHARED_INSTANCE_USING_BLOCK(block) \
2.static dispatch_once_t onceToken = 0; \
3.__strong static id sharedInstance = nil; \
4.dispatch_once(&onceToken, ^{ \
5.sharedInstance = block(); \
6.}); \
7.return sharedInstance; \
实例化的实现方法如下所示:
1.+ (BVARCSingleton *) sharedInstance
2.{
3. DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
4. return [[self alloc] init];
5. });
6.}
-------------------------------------附-----------------------------------
利用一下两种方式同样可以实现该功能:
1,
在AppDelegate中声明并初始化全局变量,
然后在需要使用该变量的地方插入如下的代码:
//取得AppDelegate,在iOS中,AppDelegat被设计成了单例模式
xxxAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.Your Variable;即可。
2,(未验证)
使用 extern 关键字
FMDatabase *db ;
在AppDelegate.m或AppDelegate.h中写入你需要的全局变量名,例如:
//数据库实例
注意:在定义全局变量的时候不能初始化,否则会报错!
在需要调用的.M文件中声明
extern FMDatabase *db ;
使用全局变量:和使用普通变量一样使用。