Objective-C中单例模式的实现【转】

单例模式在Cocoa和Cocoa Touch中非常常见。比如这两个,[UIApplication sharedApplication][NSApplication sharedApplication],大家应该都见过。但是我们应该如何在代码中实现一个单例模式呢?

如果你对苹果的文档很熟悉的话,你一定知道,在Cocoa Foundamentals Guide中有一段实现单例模式的示例代码。大致如下:

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
            
/* Singleton.h */
            #import <Foundation/Foundation.h>
            
            @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实现,中规中矩。不过这种实现并不是线程安全的。所以各路大神都各显神威,给出了多种单例模式的实现。

Matt Gallagher在博客中放出了一个Macro,用来实现单例模式。虽然是一个宏定义的代码,但是具体实现还是很清楚的。代码如下:

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
            
//
            //  SynthesizeSingleton.h
            //  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.
            //
            
            #define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
             \
            static classname *shared##classname = nil; \
             \
            + (classname *)shared##classname \
            { \
                @synchronized(self) \
                { \
                    if (shared##classname == nil) \
                    { \
                        shared##classname = [[self alloc] init]; \
                    } \
                } \
                 \
                return shared##classname; \
            } \
             \
            + (id)allocWithZone:(NSZone *)zone \
            { \
                @synchronized(self) \
                { \
                    if (shared##classname == nil) \
                    { \
                        shared##classname = [super allocWithZone:zone]; \
                        return shared##classname; \
                    } \
                } \
                 \
                return nil; \
            } \
             \
            - (id)copyWithZone:(NSZone *)zone \
            { \
                return self; \
            } \
             \
            - (id)retain \
            { \
                return self; \
            } \
             \
            - (NSUInteger)retainCount \
            { \
                return NSUIntegerMax; \
            } \
             \
            - (void)release \
            { \
            } \
             \
            - (id)autorelease \
            { \
                return self; \
            }
            

然而,eschaton则觉得这些实现都太繁琐了,他给出的实现如下:

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
            
@interface SomeManager : NSObject
            + (id)sharedManager;
            @end
            
            /* 非线程安全的实现 */
            @implementation SomeManager
            
            + (id)sharedManager {
                static id sharedManager = nil;
            
                if (sharedManager == nil) {
                    sharedManager = [[self alloc] init];
                }
            
                return sharedManager;
            }
            @end
            
            /* 线程安全的实现 */
            @implementation SomeManager
            
            static id sharedManager = nil;
            
            + (void)initialize {
                if (self == [SomeManager class]) {
                    sharedManager = [[self alloc] init];
                }
            }
            
            + (id)sharedManager {
                return sharedManager;
            }
            @end
            

关于为什么上述代码就能实现单例模式,以及关于线程安全问题的考量,请参考他的博客

最后介绍一个比较现代的单例模式实现。为什么说现代呢?因为这种实现利用了GCD(Grand Central Dispatch)和ARC(Automatic Reference Counting)。核心代码如下:

1
            2
            3
            4
            5
            6
            7
            8
            9
            
+ (id)sharedInstance
            {
              static dispatch_once_t pred = 0;
              __strong static id _sharedObject = nil;
              dispatch_once(&pred, ^{
                _sharedObject = [[self alloc] init]; // or some other init method
              });
              return _sharedObject;
            }
            

作者还写了一个宏(gist)来方便使用,大家可以阅读作者的博文A note on Objective-C singletons了解详情。

大多数情况下,Apple官方文档里的单例模式的示例代码实现已经够用了。虽然它最繁琐,但是也是本文介绍的几种单例模式中最容易理解的一个。至于其他的实现就留给读者们根据需要选择和应用了。