小鹏的技术博客

求索

Hi,我是小鹏,Java/iOS/Android开发者!


希望在这里记录一些自己对技术的学习和思考,欢迎交流!

设计模式之单例模式Singleton

1.何为单例模式(Singleton)

保证一个类仅有一个实例,并提供一个访问它的全局访问点。 一个全局变量使得一个对象可以被访问,但它不能防止实例化多个对象。一个更好的办法是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法。这就是Singleton模式。

2.如何实现单例(Objective-C)

实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

在Objective-C中要实现一个单例需要以下步骤:

  • 在单例类中声明一个单例类的静态实例,并初始化为nil;
  • 在单例类中实现一个类工厂方法(方法名类似“sharedInstance” 或者“sharedManager”),当唯一实例为nil时首先创建再返回这个唯一实例,如果唯一实例已经创建则直接返回这个唯一实例;
  • 覆盖 allocWithZone: 方法,以防止复制生成实例副本,而只生成这个共享的唯一实例;
  • 覆盖 releaseretainretainCountautorelease 内存管理方法,其中在retain和autorelease什么都不做只是返回自己,release的时候什么都不做,将retainCount设为NSUInteger的极大值。
/* Singleton.h */
#import <Foundation/Foundation>

@interface Singleton : NSObject
+ (Singleton *)instance;
@end

/* Singleton.m */
#import "Singleton.h"
static Singleton *instance = nil;

@implementation Singleton

+ (Singleton *)instance {
    if (!instance) {
        instance = [[super allocWithZone:NULL] init];
    }
    return instance;
}

+ (id)allocWithZone:(NSZone *)zone {
    return [self instance];
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)init {
    if (instance) {
        return instance;
    }
    self = [super init];
    return self;
}

- (id)retain {
    return self;
}

- (oneway void)release {
    // Do nothing
}

- (id)autorelease {
    return self;
}

- (NSUInteger)retainCount {
    return NSUIntegerMax;
}

@end

这是一种很标准的Singleton实现,不过这种实现并不是线程安全的。

注:

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。

加入线程安全的单例实现:

/* Singleton.h */
#import <Foundation/Foundation>

@interface Singleton : NSObject
+ (Singleton *)instance;
@end

/* Singleton.m */
#import "Singleton.h"
static Singleton *instance = nil;

@implementation Singleton

+ (Singleton *)instance {
    @synchronized(self) {
    	if (!instance) {
        	instance = [[super allocWithZone:NULL] init];
    	}
    }
    return instance;
}

+ (id)allocWithZone:(NSZone *)zone {
	 @synchronized(self) {  
     	return [self instance];
     }
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)init {
    if (instance) {
        return instance;
    }
    self = [super init];
    return self;
}

- (id)retain {
    return self;
}

- (oneway void)release {
    // Do nothing
}

- (id)autorelease {
    return self;
}

- (NSUInteger)retainCount {
    return NSUIntegerMax;
}

@end

这是一种线程安全的单例实现,但以上两种实现方式都是手动管理内存的方式,iOS5之后ARC技术被引入并普遍被使用,下面给出结合GCD实现ARC单例:

#import <Foundation/Foundation.h>

@interface Singleton : NSObject

+(instancetype) sharedInstance;

@end

#import "MySingleton.h"

@implementation Singleton

+(instancetype) sharedInstance {
    static dispatch_once_t pred;
    static id shared = nil;
    dispatch_once(&pred, ^{
        shared = [[super alloc] initUniqueInstance];
    });
    return shared;
}

-(instancetype) initUniqueInstance {
    return [super init];
}

@end

使用block dispatch_once,正如其名称所暗示的,在应用程序生命周期内,这方法只执行一次,这将确保仅创建一个实例,这也是快速和线程安全的:不需要检查synchronizednil。以上就是ARC下结合GCD的一个单例的代码。

再进一步,还可以写一个方便调用的宏,如下:

+ (id)sharedInstance
{
  DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
    return [[self alloc] init];
  });
}

3.应用(Cocoa中的Singleton)

在Cocoa框架中,有许多单例模式的应用,例如:NSFileManager,NSWorkspace。而UIKit框架中,同样不乏单例模式的应用,例如:UIApplication和UIAccelerometer。按照惯例,这些单例类都是通过形如sharedClassType的工厂方法返回一个共享实例。Cocoa框架的例子是sharedFileManagersharedColorPanelsharedWorkspace。我们经常使用的[NSUserDefaults standardUserDefaults][UIScreen mainScreen] 也是单例的两个应用。

参考:
A note on Objective-C singletons
Singleton of Cocoa Core Competencies
Creating a Singleton Instance
Singletons in Objective-C
Singleton with ARC
单例模式

最近的文章

设计模式之适配器模式Adapter

1.何为适配器模式(Adapter)将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作(Adapter有助于两个不兼容的接口一起工作)。简单的说,就是需要的东西就在面前,但却不能使用,而短时间又无法改造它,于是我们就想办法适配它。适配器主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况,比如在需要对早期代码复用一些功能等应用上很有实际价值。2.如何实现适配器(Objective-C)Delegate是Ob...…

继续阅读
更早的文章

iOS核心技术之:内存管理之一基本概念

内存模型isa指针在Objective-C语言的内部,每一个对象都有一个名为isa的指针,指向该对象的类。每一个类描述了一系列它的实例的特点,包括成员变量的列表,成员函数的列表等。每一个对象都可以接受消息,而对象能够接收的消息列表是保存在它所对应的类中。关于isa指针详细内容推荐巧哥的 Objective-C对象模型及应用 介绍的非常到位并且包含实际应用场景。堆内存    堆(heap)是指内存中的一块区域,应用中的所有对象都会保存在堆中。当应用向某个类发送alloc消息时,系统会从堆中分...…

继续阅读