• iOS设计模式:单例模式


     

    因为单例是全局哪里要用直接调用就行非常方便简单,一般我们可以用单例来作对用户信息的存储,其次单例可以做成购物车之类的页面等等。当然单例最大的优势个人感觉就是对数据的存储和读取非常方便,就可以解决页面之间传值困难的问题。简单讲下怎样用单例对数据传输吧,把需要的数据都定义成属性,当需要存储的时候直接调用存储就行,要用的时候把它调出使用就行了这里不做过多描述了。

     

    单例模式是一个类在系统中只有一个实例对象。通过全局的一个入口点对这个实例对象进行访问。在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中,单例有两种实现方式(至少我目前只发现两种)。根据线程安全的实现来区分,一种是使用@synchronized,另一种是使用GCD的dispatch_once函数。

    要实现单例,首先需要一个static的指向类本身的对象,其次需要一个初始化类函数。下面是两种实现的代码。

    1、@synchronized

    static InstanceClass *instance;
    + (InstanceClass *)defaultInstance{
        @synchronized (self){
            if (instance == nil) {
                instance = [[InstanceClass alloc] init];
            }
        }
        
        return instance;
    }
    

    2、GCD

    static InstanceClass *instance;
    + (InstanceClass *)defaultInstance{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[InstanceClass alloc] init];
        });
        
        return instance;
    }
    

    总的来说,两种实现效果相同,但第二种GCD的实现方式写起来比较简单。如果不习惯GCD的方式,可以使用第一种方式。

    无论是爱还是恨,你都需要单例。实际上每个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];
        有些时候你希望有这种行为,但如果正在想要的是仅一个实例被实例化就需要注意这点。
     
     

    单例模式介绍

    单例模式确保在一个应用中只产生一个实例,这是很有必要的,因为在我们做软件设计的时候,有很多对象都是只需要一个就可以了,而不需要创建众多的对象,这样最显而易见的就是节省了内存空间。而且避免了这个类的频繁的初始化与销毁。有时为了实现某一种功能与操作而创建的类(工具类)往往也不需要多个对象,使用单例模式再合适不过。再延伸一点,有时为了节省内存对一个对象进行复用的话也可以通过单例来实现,这在手机软件的开发中用得比较多,因为手机的内存实在是少得可怜。

    单例模式优点

    1. 正如前面说的,单例模式在内存中只有一个实例,减少了内存开支。特别是一个对象需要频繁的创建、销毁时,而创建与销毁的性能有无法优化,单例模式的优势就非常明显。
    2. 单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
    3. 单例模式可以避免对资源的多重占用。
    4. 单例模式可以在系统设置全局的访问点,优化和共享资源访问。

    单例模式缺点

    1. 单例模式一般没有接口,扩展很困难,除了修改代码基本上没有第二种途径实现。
    2. 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的。
    3. 单例模式与单一职责原则有冲突。

    单例模式在iOS中的使用

    单例模式在iOS开发中的使用还是蛮多的,许多FoundationCocoaUIKit中的类都实现了单例模式,比如应用程序本身UIApplication、文件操作类NSFileManager、消息中心NSNotificitonCenter等系统都已经给我们实现单例,我们只需要使用就好了。在iOS中使用单例模式要使用类方法,通过类方法返回该类的唯一对象。

    我知道的在iOS开发中实现单例模式主要有以下三种方式:

    第一种

    该方法是苹果的官方文档中写的一种方式,通过覆盖NSObject的部分方法实现,使该类无法allocretainrelease。这是最麻烦的一种方法,也是最不好的一种方法。

    Singleton
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    static Singleton *instance = nil;
    
    + (Singleton *)sharedInstance
    {
        if (instance == nil) {
            instance = [[super allocWithZone:NULL] init];
        }
        return instance;
    }
    
    + (id)allocWithZone:(NSZone *)zone
    {
        return [[self sharedInstance] retain];
    }
    
    - (id)copyWithZone:(NSZone *)zone
    {
        return self;
    }
    
    - (id)retain
    {
        return self;
    }
    
    - (NSUInteger)retainCount
    {
        return NSUIntegerMax;  //denotes an object that cannot be released
    }
    
    - (void)release
    {
        //do nothing
    }
    
    - (id)autorelease
    {
        return self;
    }
    

    可以看到这种方式,使用静态成员维持了一个永久存在的对象,而且覆盖了alloc方法(alloc方法会调用allocWithZone:方法),并且也覆盖了所有与引用技术有关的方法,这都使这个对象不会被销毁。这样看上去基本实现了我们需要的,但是写起来麻烦不说,还有很大的一个问题,那就是多线程问题,如果是在多线程中那么该种方法就不能保证只产生一个对象了。所以这种方式只是介绍一下,并不推荐使用。

    第二种

    第二种跟第一种差不多,也是通过覆盖NSObject的方法实现的,但是它在第一种的基础上增加了多线程的处理,所以即使在多线程下,该种方法创建的对象也是唯一的。这种方法已经有大牛为我们写好了,全都都是通过C的宏定义#define出来了。现给出该头文件:

    (SynthesizeSingleton.h)download
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    
    //
    //  SynthesizeSingleton.h
    //
    // Modified by Karl Stenerud starting 16/04/2010.
    // - Moved the swizzle code to allocWithZone so that non-default init methods may be
    //   used to initialize the singleton.
    // - Added "lesser" singleton which allows other instances besides sharedInstance to be created.
    // - Added guard ifndef so that this file can be used in multiple library distributions.
    // - Made singleton variable name class-specific so that it can be used on multiple classes
    //   within the same compilation module.
    //
    //  Modified by CJ Hanson on 26/02/2010.
    //  This version of Matt's code uses method_setImplementaiton() to dynamically
    //  replace the +sharedInstance method with one that does not use @synchronized
    //
    //  Based on code by Matt Gallagher from CocoaWithLove
    //
    //  Created by Matt Gallagher on 20/10/08.
    //  Copyright 2009 Matt Gallagher. All rights reserved.
    //
    //  Permission is given to use this source code file without charge in any
    //  project, commercial or otherwise, entirely at your risk, with the condition
    //  that any redistribution (in part or whole) of source code must retain
    //  this copyright and permission notice. Attribution in compiled projects is
    //  appreciated but not required.
    //
    
    #ifndef SYNTHESIZE_SINGLETON_FOR_CLASS
    
    #import <objc/runtime.h>
    
    
    #pragma mark -
    #pragma mark Singleton
    
    /* Synthesize Singleton For Class
     *
     * Creates a singleton interface for the specified class with the following methods:
     *
     * + (MyClass*) sharedInstance;
     * + (void) purgeSharedInstance;
     *
     * Calling sharedInstance will instantiate the class and swizzle some methods to ensure
     * that only a single instance ever exists.
     * Calling purgeSharedInstance will destroy the shared instance and return the swizzled
     * methods to their former selves.
     *
     *
     * Usage:
     *
     * MyClass.h:
     * ========================================
     *      #import "SynthesizeSingleton.h"
     *
     *      @interface MyClass: SomeSuperclass
     *      {
     *              ...
     *      }
     *      SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(MyClass);
     *
     *      @end
     * ========================================
     *
     *
     *      MyClass.m:
     * ========================================
     *      #import "MyClass.h"
     *
     *      // This line is optional. Use it if you've enabled GCC_WARN_UNDECLARED_SELECTOR
     *      SYNTHESIZE_SINGLETON_FOR_CLASS_PROTOTYPE(MyClass);
     *
     *      @implementation MyClass
     *
     *      SYNTHESIZE_SINGLETON_FOR_CLASS(MyClass);
     *
     *      ...
     *
     *      @end
     * ========================================
     *
     *
     * Note: Calling alloc manually will also initialize the singleton, so you
     * can call a more complex init routine to initialize the singleton like so:
     *
     * [[MyClass alloc] initWithParam:firstParam secondParam:secondParam];
     *
     * Just be sure to make such a call BEFORE you call "sharedInstance" in
     * your program.
     */
    
    #define SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(SS_CLASSNAME)     
    
    + (SS_CLASSNAME*) sharedInstance;       
    + (void) purgeSharedInstance;
    
    
    #define SYNTHESIZE_SINGLETON_FOR_CLASS_PROTOTYPE(SS_CLASSNAME) 
    @interface SS_CLASSNAME (SynthesizeSingletonPrivate)    
    - (NSUInteger)retainCountDoNothing;     
    - (NSUInteger)retainCountDoSomething;   
    - (void)releaseDoNothing;       
    - (void)releaseDoSomething;     
    - (id)autoreleaseDoNothing;     
    - (id)autoreleaseDoSomething; 
    @end
    
    #define SYNTHESIZE_SINGLETON_FOR_CLASS(SS_CLASSNAME)    
    
    static volatile SS_CLASSNAME* _##SS_CLASSNAME##_sharedInstance = nil;   
    
    + (volatile SS_CLASSNAME*) sharedInstanceNoSynch        
    {       
    return (volatile SS_CLASSNAME*) _##SS_CLASSNAME##_sharedInstance;       
    }       
    
    + (volatile SS_CLASSNAME*) sharedInstanceSynch  
    {       
    @synchronized(self)     
    {       
    if(nil == _##SS_CLASSNAME##_sharedInstance)     
    {       
    _##SS_CLASSNAME##_sharedInstance = [[self alloc] init]; 
    }       
    }       
    return (volatile SS_CLASSNAME*) _##SS_CLASSNAME##_sharedInstance;       
    }       
    
    + (volatile SS_CLASSNAME*) sharedInstance       
    {       
    return (volatile SS_CLASSNAME*)[self sharedInstanceSynch]; 
    }       
    
    + (id)allocWithZone:(NSZone*) zone      
    {       
    @synchronized(self)     
    {       
    if (nil == _##SS_CLASSNAME##_sharedInstance)    
    {       
    _##SS_CLASSNAME##_sharedInstance = [super allocWithZone:zone];  
    if(nil != _##SS_CLASSNAME##_sharedInstance)     
    {       
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));  
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));     
    method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoNothing)));  
    method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoNothing)));  
    method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoNothing)));  
    }       
    }       
    }       
    return (id)_##SS_CLASSNAME##_sharedInstance;    
    }       
    
    + (void)purgeSharedInstance     
    {       
    @synchronized(self)     
    {       
    if(nil != _##SS_CLASSNAME##_sharedInstance)     
    {       
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceSynch));    
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));     
    method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoSomething)));        
    method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoSomething)));        
    method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoSomething)));        
    [_##SS_CLASSNAME##_sharedInstance release];     
    _##SS_CLASSNAME##_sharedInstance = nil; 
    }       
    }       
    }       
    
    - (id)copyWithZone:(NSZone *)zone       
    {       
    return self;    
    }       
    
    - (id)retain    
    {       
    return self;    
    }       
    
    - (NSUInteger)retainCount       
    {       
    NSAssert1(1==0, @"SynthesizeSingleton: %@ ERROR: -(NSUInteger)retainCount method did not get swizzled.", self); 
    return NSUIntegerMax;   
    }       
    
    - (NSUInteger)retainCountDoNothing      
    {       
    return NSUIntegerMax;   
    }       
    - (NSUInteger)retainCountDoSomething    
    {       
    return [super retainCount];     
    }
    
    - (oneway void)release  
    {       
    NSAssert1(1==0, @"SynthesizeSingleton: %@ ERROR: -(void)release method did not get swizzled.", self);   
    }       
    
    - (void)releaseDoNothing{}      
    
    - (void)releaseDoSomething      
    {       
    @synchronized(self)     
    {       
    [super release];        
    }       
    }       
    
    - (id)autorelease       
    {       
    NSAssert1(1==0, @"SynthesizeSingleton: %@ ERROR: -(id)autorelease method did not get swizzled.", self); 
    return self;    
    }       
    
    - (id)autoreleaseDoNothing      
    {       
    return self;    
    }       
    
    - (id)autoreleaseDoSomething    
    {       
    return [super autorelease];     
    }
    
    
    #pragma mark -
    #pragma mark Lesser Singleton
    
    /* A lesser singleton has a shared instance, but can also be instantiated on its own.
     *
     * For a lesser singleton, you still use SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(),
     * but use SYNTHESIZE_LESSER_SINGLETON_FOR_CLASS() in the implementation file.
     * You must specify which creation methods are to initialize the shared instance
     * (besides "sharedInstance") via CALL_LESSER_SINGLETON_INIT_METHOD()
     *
     * Example:
     *
     * MyClass.h:
     * ========================================
     *      #import "SynthesizeSingleton.h"
     *
     *      @interface MyClass: SomeSuperclass
     *      {
     *              int value;
     *              ...
     *      }
     *      SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(MyClass);
     *
     *      + (void) initSharedInstanceWithValue:(int) value;
     *
     * - (id) initWithValue:(int) value;
     *
     *      @end
     * ========================================
     *
     *
     *      MyClass.m:
     * ========================================
     *      #import "MyClass.h"
     *
     *      // This line is optional. Use it if you've enabled GCC_WARN_UNDECLARED_SELECTOR
     *      SYNTHESIZE_SINGLETON_FOR_CLASS_PROTOTYPE(MyClass);
     *
     *      @implementation MyClass
     *
     *      SYNTHESIZE_LESSER_SINGLETON_FOR_CLASS(MyClass);
     *
     *      + (void) initSharedInstanceWithValue:(int) value
     *      {
     *              CALL_LESSER_SINGLETON_INIT_METHOD(MyClass, initWithValue:value);
     *      }
     *
     *      ...
     *
     *      @end
     * ========================================
     *
     *
     * Note: CALL_LESSER_SINGLETON_INIT_METHOD() will not work if your
     * init call contains commas. If you need commas (such as for varargs),
     * or other more complex initialization, use the PRE and POST macros:
     *
     *      + (void) initSharedInstanceComplex
     *      {
     *              CALL_LESSER_SINGLETON_INIT_METHOD_PRE(MyClass);
     *
     *              int firstNumber = [self getFirstNumberSomehow];
     *              _sharedInstance = [[self alloc] initWithValues:firstNumber, 2, 3, 4, -1];
     *
     *              CALL_LESSER_SINGLETON_INIT_METHOD_POST(MyClass);
     *      }
     */
    #define SYNTHESIZE_LESSER_SINGLETON_FOR_CLASS(SS_CLASSNAME)     
    
    static volatile SS_CLASSNAME* _##SS_CLASSNAME##_sharedInstance = nil;   
    
    + (SS_CLASSNAME*) sharedInstanceNoSynch 
    {       
    return (SS_CLASSNAME*) _##SS_CLASSNAME##_sharedInstance;        
    }       
    
    + (SS_CLASSNAME*) sharedInstanceSynch   
    {       
    @synchronized(self)     
    {       
    if(nil == _##SS_CLASSNAME##_sharedInstance)     
    {       
    _##SS_CLASSNAME##_sharedInstance = [[self alloc] init]; 
    if(_##SS_CLASSNAME##_sharedInstance)    
    {       
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));  
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));     
    }       
    }       
    }       
    return (SS_CLASSNAME*) _##SS_CLASSNAME##_sharedInstance;        
    }       
    
    + (volatile SS_CLASSNAME*)sharedInstance        
    {       
    return (volatile SS_CLASSNAME*) [self sharedInstanceSynch]; 
    }       
    
    + (void)purgeSharedInstance     
    {       
    @synchronized(self)     
    {       
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceSynch));    
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));     
    [_##SS_CLASSNAME##_sharedInstance release];     
    _##SS_CLASSNAME##_sharedInstance = nil; 
    }       
    }
    #define CALL_LESSER_SINGLETON_INIT_METHOD_PRE(SS_CLASSNAME) 
    @synchronized(self)     
    {       
    if(nil == _##SS_CLASSNAME##_sharedInstance)     
    {
    #define CALL_LESSER_SINGLETON_INIT_METHOD_POST(SS_CLASSNAME) 
    if(_##SS_CLASSNAME##_sharedInstance)    
    {       
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));  
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));     
    }       
    }       
    }
    #define CALL_LESSER_SINGLETON_INIT_METHOD(SS_CLASSNAME,__INIT_CALL__) 
    CALL_LESSER_SINGLETON_INIT_METHOD_PRE(SS_CLASSNAME); 
    _##SS_CLASSNAME##_sharedInstance = [[self alloc] __INIT_CALL__];        
    CALL_LESSER_SINGLETON_INIT_METHOD_POST(SS_CLASSNAME)
    #endif /* SYNTHESIZE_SINGLETON_FOR_CLASS */

    使用时也非常方便,该头文件也已给出使用方法,在这里我在说一下,供那些E文不好的同学使用。

    使用这种方式首先把该头文件加到我们的项目中,然后直接使用就可以了:

    Singleton.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    #import <Foundation/Foundation.h>
    #import "SynthesizeSingleton.h"
    
    @interface Singleton : NSObject
    
    SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(Singleton);
    
    //定义该类的属性,方法等
    
    @end
    
    Singleton.m
    1
    2
    3
    4
    5
    6
    7
    
    @implementation Singleton
    
    SYNTHESIZE_SINGLETON_FOR_CLASS(Singleton);
    
    //属性方法的实现
    
    @end
    

    如此一来在使用时,通过[Singleton sharedInstance]就可以获得该类的单例对象了。 这种方法由于有了这个头文件的支持,所以使得使用单例方便多了,而且也避免了多线程的问题。

    第三种

    这是最后一种也是我最推荐的一种。iOS在4.0以后推出了blockGCD,这两个特性给iOS开发带来的很大的便利,也使开发变得更加趣味话。那么如何通过GCD+block来实现单例模式呢,这主要归功于dispatch_once(dispatch_once_t *predicate, ^(void)block)这个GCD的函数,他有两个参数第一参数是一个指向dispatch_once_t类型结构体的指针,用来测试block是否执行完成,该指针所指向的结构体必须是全局的或者静态的,第二个参数是一个返回值与参数均为空的block,在block体中进行对象的初始化即可。dispatch_once在程序的生命周期中保证只会被调用一次,所以在多线程中也不会有问题。 该种方法使用方法:

    Singleton
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    + (Singleton *)sharedInstance
    {
        static Singleton *instance = nil;
    
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[Singleton alloc]init];
        });
    
        return instance;
    }
    

    使用该种方法只需要这简单的几句代码就可以实现单例了。使用起来非常方便,但是这种创建单例的方法也不是完美的,它并不能阻止人们通过alloc方法来实例化一个对象,所以这并不是严格意义上的单例模式,但是一般程序都是我们自己写,我们自己记得就好了,这也没什么可担心的,从这一点上来说第二种方法又是比较好的,具体使用的时候呢,根据实际情况来吧,各取所需就好了。

  • 相关阅读:
    flutter常用内置动画组件
    flutter中的生命周期函数
    Flutter打开第三方应用
    在windows系统搭建并运行一个Flutter项目
    在windows系统搭建Flutter开发环境
    axios的get请求无法设置Content-Type
    解决vue中使用laydate.js选择日期后再修改其他model时日期会被清空问题
    Git commit时提示错误时    解决办法
    CSS3 @font-face属性
    解决webstorm本地IP访问页面出错的问题
  • 原文地址:https://www.cnblogs.com/fengmin/p/4971409.html
Copyright © 2020-2023  润新知