IOS单例模式

IOS单例模式(Singleton)

 

单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。

1.单例模式的要点:

  显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。

2.单例模式的优点:

  1.实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例。
  2.灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
 
IOS中的单例模式
  在objective-c中要实现一个单例类,至少需要做以下四个步骤:
  1、为单例对象实现一个静态实例,并初始化,然后设置成nil,
  2、实现一个实例构造方法检查上面声明的静态实例是否为nil,如果是则新建并返回一个本类的实例,
  3、重写allocWithZone方法,用来保证其他人直接使用alloc和init试图获得一个新实力的时候不产生一个新实例,
  4、适当实现allocWitheZone,copyWithZone,release和autorelease。
下面以SurveyRunTimeData为例子:
 
static SurveyRunTimeData *sharedObj = nil; //第一步:静态实例,并初始化。
@implementation SurveyRunTimeData
+ (SurveyRunTimeData*) sharedInstance  //第二步:实例构造检查静态实例是否为nil
{
    @synchronized (self)
    {
        if (sharedObj == nil)
        {
            [[self alloc] init];
        }
    }
    return sharedObj;
}

+ (id) allocWithZone:(NSZone *)zone //第三步:重写allocWithZone方法
{
    @synchronized (self) {
        if (sharedObj == nil) {
            sharedObj = [super allocWithZone:zone];
            return sharedObj;
        }
    }
    return nil;
}

- (id) copyWithZone:(NSZone *)zone //第四步
{
    return self;
}

- (id) retain
{
    return self;
}

- (unsigned) retainCount
{
    return UINT_MAX;
}

- (oneway void) release
{
    
}

- (id) autorelease
{
    return self;
}

- (id)init
{
    @synchronized(self) {
        [super init];//往往放一些要初始化的变量.
        return self;
    }
 
 
 

iOS设计模式——单例模式

分类: iOS
 

目录(?)[+]

 

 

单例模式用于当一个类只能有一个实例的时候, 通常情况下这个“单例”代表的是某一个物理设备比如打印机,或是某种不可以有多个实例同时存在的虚拟资源或是系统属性比如一个程序的某个引擎或是数据。用单例模式加以控制是非常有必要的。

单例模式需要达到的目的

1. 封装一个共享的资源

2. 提供一个固定的实例创建方法

3. 提供一个标准的实例访问接口

单例模式的创建

本文以创建一个MySingletonClass的单例模式为例。首先,我们需要定义一个类MySingletonClass.

  1. @interface MySingletonClass:NSObject {  
  2.       
  3. }  

并且为其添加一个类方法(注意,这里不是实例方法)+(id)sharedInstance;一个基本的实现写法如下:

  1. static MySingletonClass *sharedCLDelegate = nil;  
  2. +(MySingletonClass *)sharedInstance{  
  3.     @synchronized(self) {  
  4.         if(sharedCLDelegate == nil) {  
  5.             [[[self class] alloc] init]; //   assignment   not   done   here  
  6.         }  
  7.     }  
  8.     return sharedCLDelegate;  
  9. }  

在上面的代码中(用到了关键字@synchronized是为了保证我们的单例的线程级别的安全,可以适用于多线程模式下。)static变量sharedCLDelegate用于存储一个单例的指针,并且强制所有对该变量的访问都必须通过类方法   +(id)sharedInstance,在对   +(id)sharedInstance第一次调用时候完成实例的创建。这里值得留意一下的是,上面代码中用的是[[selfclass] alloc],而不是 [MySingletonClass alloc],一般情况下这两种写法产生同样的效果,但是这里这样做是为了更好的利用OOP的性质,[selfclass]可以动态查找并确定类的类型从而便于实现对该类的子类化。

对实例化的控制

为了完全的实现实例的单态性,必须通过一定手段来避免实例多次被创建。+(id)sharedInstance控制了单例的创建和访问,但是并不能控制其它地方的代码通过alloc方法来创建更多的实例,因此我们还要重载任何一个涉及到allocation的方法,这些方法包括   +new, +alloc,+allocWithZone:, -copyWithZone:, 以及 -mutableCopyWithZone: 另外,+(id)sharedInstance也需要稍作修改。

  1. + (id)hiddenAlloc  
  2. {  
  3.     return [super alloc];  
  4. }  
  5.   
  6.   
  7. + (id)alloc  
  8. {  
  9.     NSLog(@"%@: use +sharedInstance instead of +alloc", [[self class] name]);  
  10.     return nil;  
  11. }  
  12.   
  13.   
  14. + (id)new  
  15. {  
  16.     return [self alloc];  
  17. }  
  18.   
  19. +(id)allocWithZone:(NSZone*)zone  
  20. {  
  21.     return [self alloc];  
  22. }  
  23.   
  24. -   (id)copyWithZone:(NSZone *)zone  
  25. {   // -copy inherited from NSObject calls -copyWithZone:  
  26.     NSLog(@"MySingletonClass: attempt to -copy may be a bug.");  
  27.     [self retain];  
  28.     return self;  
  29. }  
  30.   
  31. - (id)mutableCopyWithZone:(NSZone *)zone  
  32. {  
  33.     // -mutableCopy inherited from NSObject calls -mutableCopyWithZone:  
  34.     return [self copyWithZone:zone];  
  35. }  
  36.   
  37. +(id)sharedInstance修改如下:  
  38.   
  39. + (MySingletonClass *)sharedInstance {  
  40.     @synchronized(self) {  
  41.         if (sharedCLDelegate == nil)   {  
  42.             [[[self class] hiddenAlloc] init]; // assignment not done here  
  43.         }  
  44.     }  
  45.     return sharedCLDelegate;  
  46. }  

如果不考虑类的子类化,+hiddenAlloc这个方法可以省略。由于我们是用[selfclass]来实现类型的动态识别,用[[selfclass] hiddenAlloc]可以避免调用到被重载过的alloc方法。此外,hiddenAlloc也为可能的子类化提供了一个调用原始alloc方法的机会。上面重载过的alloc方法只是给出一个log信息并且返回nil。Copying方法里只是简单的增加了retain的计数并没有返回一个新的实例。这也正体现了单例模式的性质,因为技术上来讲,拷贝一个单例是错误的(因为是“单例”)所以在copyWithZone方法中我们给出了一个错误信息,当然也可以扔出一个exception。

单例的销毁

通常我们在   -(void)applicationWillTerminate:(UIApplication *)application方法中调用如下方法:

  1. + (void)attemptDealloc  
  2. {  
  3.     if ([sharedCLDelegate retainCount] != 1)  
  4.         return;  
  5.   
  6.     [sharedCLDelegate release];  
  7.     myInstance = nil;  
  8. }  

值得注意的是,上面这个attemptDealloc方法顾名思义,只是试图释放掉这个单例。如果retain的计数不为1,说明还有其他地方对该单例发送过retain消息。考虑到一个单例模式的生存周期是整个程序结束为止。所以,在程序的任何一个地方都没有必要向这个单例发送retain消息,即便是对这个单例有引用。而是调用sharedInstance方法来引用这个单例,这样做是安全的,也是合乎单例模式的技术含义的。

iOS中的单例模式应用

iOS中好几个类都是采用了单例模式,比如NSApplication, NSFontManager,   NSDocumentController,NSHelpManager, NSNull,NSProcessInfo, NSScriptExecutionContext,   NSUserDefaults.

 

如果本文有任何错误之处,欢迎拍砖指正,共同进步, 谢谢!

iOS设计模式(02):单例模式

发布于:2013-05-10 09:58阅读数:7735

单例模式是一个类在系统中只有一个实例对象。通过全局的一个入口点对这个实例对象进行访问。在iOS开发中,单例模式是非常有用的一种设计模式。如下图,是一个简单的例模式的UML类图。

 
阅读器

 


 

什么是单例模式?

单例模式是一个类在系统中只有一个实例对象。通过全局的一个入口点对这个实例对象进行访问。在iOS开发中,单例模式是非常有用的一种设计模式。如下图,是一个简单的例模式的UML类图。

 

iOS SDK中也有许多类使用了单例模式,例如,UIApplication:当程序启动的时候,会调用UIApplicationMain方法,在该方法中,会实例化一个UIApplication对象,之后在程序中的任意地方调用sharedApplication方法都将返回一个与当前应用程序相关的UIApplication实例(UIApplicationMain方法中创建的UIApplication单例)。

 

什么时候使用单例模式?

在程序中,单例模式经常用于只希望一个类只有一个实例,而不运行一个类还有两个以上的实例。当然,在iOS SDK中,根据特定的需求,有些类不仅提供了单例访问的接口,还为开发者提供了实例化一个新的对象接口,例如,NSFileManager可以通过defaultManager方法返回相同的一个NSFileManager对象。如果需要新的一个NSFileManager实例对象,可以通过init方法。

 

iOS中单例模式的实现

iOS中单例模式的实现方式一般分为两种:Non-ARC(非ARC)和ARC+GCD。


1.Non-ARC(非ARC)
非ARC的实现方法如下所示:

BVNonARCSingleton.h
1.//
2.//  BVNonARCSingleton.h
3.//  SingletonPattern
4.//
5.//  Created by BeyondVincent on 13-5-9.
6.//  Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8. 
9.#import <Foundation/Foundation.h>
10. 
11.@interface BVNonARCSingleton : NSObject
12. 
13.@property  ( nonatomic, retain) NSString  *tempProperty;
14.+ (BVNonARCSingleton *)sharedInstance; 
15. 
16.@end11.@implementation BVNonARCSingleton
12. 
13.static BVNonARCSingleton *sharedInstance = nil;
14. 
15.// 获取一个sharedInstance实例,如果有必要的话,实例化一个
16.+ (BVNonARCSingleton *)sharedInstance {
17.    if (sharedInstance == nil) {
18.        sharedInstance = [[super allocWithZone:NULL] init];
19.    }
20. 
21.    return sharedInstance;
22.}
23. 
24.// 当第一次使用这个单例时,会调用这个init方法。
25.- (id)init
26.{
27.    self = [super init];
28. 
29.    if (self) {
30.        // 通常在这里做一些相关的初始化任务
31.    }
32. 
33.    return self;
34.}
35. 
36.// 这个dealloc方法永远都不会被调用--因为在程序的生命周期内容,该单例一直都存在。(所以该方法可以不用实现)
37.-(void)dealloc
38.{
39.    [super dealloc];
40.}
41. 
42.// 通过返回当前的sharedInstance实例,就能防止实例化一个新的对象。
43.+ (id)allocWithZone:(NSZone*)zone {
44.    return [[self sharedInstance] retain];
45.}
46. 
47.// 同样,不希望生成单例的多个拷贝。
48.- (id)copyWithZone:(NSZone *)zone {
49.    return self;
50.}
51. 
52.// 什么也不做——该单例并不需要一个引用计数(retain counter)
53.- (id)retain {
54.    return self;
55.}
56. 
57.// 替换掉引用计数——这样就永远都不会release这个单例。
58.- (NSUInteger)retainCount {
59.    return NSUIntegerMax;
60.}
61. 
62.// 该方法是空的——不希望用户release掉这个对象。
63.- (oneway void)release {
64. 
65.}
66. 
67.//除了返回单例外,什么也不做。
68.- (id)autorelease {
69.    return self;
70.}
71. 
72.@end2.@synchronized (self)
3.{
4.    if(sharedInstance == nil)
5.    {
6.        sharedInstance = [[super allocWithZone:NULL] init];
7.    }
8.}11.@interface BVARCSingleton : NSObject
12. 
13.@property  ( nonatomic, weak) NSString  *tempProperty;
14.+ (BVARCSingleton *)sharedInstance;
15. 
16.@end11.@implementation BVARCSingleton
12. 
13.+ (BVARCSingleton *) sharedInstance
14.{
15.    static  BVARCSingleton *sharedInstance = nil ;
16.    static  dispatch_once_t onceToken;  // 锁
17.    dispatch_once (& onceToken, ^ {     // 最多调用一次
18.        sharedInstance = [[self  alloc] init];
19.    });
20.    return  sharedInstance;
21.}
22. 
23.// 当第一次使用这个单例时,会调用这个init方法。
24.- (id)init
25.{
26.    self = [super init];
27. 
28.    if (self) {
29.        // 通常在这里做一些相关的初始化任务
30.    }
31. 
32.    return self;
33.}
34. 
35.@end2.@synchronized (self)


BVNonARCSingleton.m
1.//
2.//  BVNonARCSingleton.m
3.//  SingletonPattern
4.//
5.//  Created by BeyondVincent on 13-5-9.
6.//  Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8. 
9.#import "BVNonARCSingleton.h"
10. 
 
实际上上面的代码苹果官网也有提供:Creating a Singleton Instance只不过没有给出头文件的定义。上面用非ARC实现单例的方法是线程不安全的,如果有多个线程同时调用sharedInstance方法获取一个实例,而sharedInstance方法需要花费1-2秒钟的时间,那么BVNonARCSingleton的init方法就可能会被多次调用,也就是不同线程获得的BVNonARCSingleton有可能不是同一个实例。怎么解决线程的不安全呢?答案是使用@synchronized来创建互斥锁即可。

1.// 保证在实例化的时候是线程安全的(当然,该方法不能保证该单例中所有方法的调用都是线程安全的)
2.@synchronized (self)
3.{
4.    if(sharedInstance == nil)
5.    {
6.        sharedInstance = [[super allocWithZone:NULL] init];
7.    }
8.}
 

通过上面的代码就能保存线程安全。
1.<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">提醒:在iOS中,一般不建议使用非ARC来实现单例模式。更好的方法是使用ARC+GCD来实现。</span>

 

2.ARC+GCD
通过ARC+GCD的方法来实现单例模式的非常简单的。下面先来看看具体实现:

BVARCSingleton.h
1.//
2.//  BVARCSingleton.h
3.//  SingletonPattern
4.//
5.//  Created by BeyondVincent on 13-5-9.
6.//  Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8. 
9.#import &lt;Foundation/Foundation.h&gt;
10. 
 
BVARCSingleton.m
1.//
2.//  BVARCSingleton.m
3.//  SingletonPattern
4.//
5.//  Created by BeyondVincent on 13-5-9.
6.//  Copyright (c) 2013年 BeyondVincent. All rights reserved.
7.//
8. 
9.#import "BVARCSingleton.h"
10. 
 
在上面的代码中,调用Grand Central Dispatch (GCD)中的dispatch_once方法就可以确保BVARCSingleton只被实例化一次。并且该方法是线程安全的,我们不用担心在不同的线程中,会获得不同的实例。(当然,该方法同样不能保证该单例中所有方法的调用都是线程安全的)。

 

当然,在ARC中,不用GCD也是可以做到线程安全的,跟之前非ARC代码中使用@synchronized一样,如下代码:
1.    // 不使用GCD,通过@synchronized
2.@synchronized (self)
3.{
4.    if(sharedInstance == nil)
5.    {
6.        sharedInstance = [[self alloc] init];
7.    }
8.}
 
为了简化使用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(&amp;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.}


单例的使用

单例的使用方法很简单,在代码中的任意位置,如下使用即可:

在BVAppDelegate.m中添加头文件:
1.#import "BVNonARCSingleton.h"
2.#import "BVARCSingleton.h"


如下使用方法:
1.- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2.{
3.    [BVNonARCSingleton sharedInstance].tempProperty = @"非ARC单例的实现";
4.    NSLog(@"%@", [BVNonARCSingleton sharedInstance].tempProperty);
5. 
6.    [BVARCSingleton sharedInstance].tempProperty = @"ARC单例的实现";
7.    NSLog(@"%@", [BVARCSingleton sharedInstance].tempProperty);
8. 
9.    return YES;
10.}


运行程序,会在控制台窗口输出如下内容:
1.2013-05-09 16:44:07.649 SingletonPattern[5159:c07] 非ARC单例的实现
2.2013-05-09 16:44:33.204 SingletonPattern[5159:c07] ARC单例的实现


代码实例
本文涉及

iOS设计模式之单例模式

 

单例模式是iOS常用设计模式中的一种。单例设计模式的作用是使得这个类的一个对象成为系统中的唯一实例,因此需要用一种唯一的方法去创建这个对象并返回这个对象的地址。那么,我们何时使用单例模式呢?1、类只能有一个实例,而且必须从一个为人熟知的访问点对其访问。2、这个唯一的实例只能通过子类化进行扩展,而且扩展的对象不会破坏客户端代码。

那么用Objective-C如何实现单例模式呢?下面我们来新建一个Singleton类,在Singleton.h中实现如下

 

  1. @interface Singleton : NSObject  
  2.   
  3. + (Singleton *) sharedInstance;  
  4.   
  5. @end  

 

 

在Singleton.m

  1. @implementation Singleton  
  2.   
  3. static Singleton * sharedSingleton = nil;  
  4.   
  5. + (Singleton *) sharedInstance  
  6. {  
  7.     if (sharedSingleton == nil) {  
  8.         sharedSingleton = [[Singleton alloc] init];  
  9.     }  
  10.     return sharedSingleton;  
  11. }  
  12.   
  13. @end  


这样就创建一个简单的单例模式,实际上有一部分程序员也是这样实现的,但实际上这是一个不“严格”版本,在实际中使用,可能会遇到发起调用的对象不能以其他分配方式实例化单例对象,否则,就会创建多个实例。(之前有人和我讨论过这个问题,说使用者应该严格按照接口来使用,当实际上Singleton是一个对象,我们不能保证使用者不会使用其他的方法去创建(比如alloc),这个时候他就会创建多个实例,这样就会出现这些无法感知的bug)

 

 

下面我对Singleton.m的进行改进

  1. @implementation Singleton  
  2.   
  3. static Singleton * sharedSingleton = nil;  
  4.   
  5. + (Singleton *) sharedInstance  
  6. {  
  7.     if (sharedSingleton == nil) {  
  8.         sharedSingleton = [[super allocWithZone:NULL] init];  
  9.     }  
  10.     return sharedSingleton;  
  11. }  
  12.   
  13. + (id) allocWithZone:(struct _NSZone *)zone  
  14. {  
  15.     return [[self sharedInstance] retain];  
  16. }  
  17.   
  18. - (id) copyWithZone:(NSZone *) zone  
  19. {  
  20.     return self;  
  21. }  
  22.   
  23. - (id) retain  
  24. {  
  25.     return self;  
  26. }  
  27.   
  28. - (NSUInteger) retainCount  
  29. {  
  30.     return NSUIntegerMax;  
  31. }  
  32.   
  33.   
  34. - (void) release  
  35. {  
  36.     //  
  37. }  
  38.   
  39. - (id) autorelease  
  40. {  
  41.     return self;  
  42. }  
  43.   
  44. @end  


也许你注意到了,我重载了allocWithZone:,保持了从sharedInstance方法返回的单例对象,使用者哪怕使用alloc时也会返回唯一的实例(alloc方法中会先调用allocWithZone:创建对象)。而retain等内存管理的函数也被重载了,这样做让我们有了把Singleton类变得“严格”了。

 
分类: ios
 
绿色通道: 好文要顶 关注我 收藏该文与我联系 
2
0
 
(请您对文章做出评价)
 
« 上一篇:使用iO
posted @ 2015-01-16 11:47  mengxiangtong22  阅读(873)  评论(0编辑  收藏  举报