关于ios:如何实现与ARC兼容的Objective-C单例?

How do I implement an Objective-C singleton that is compatible with ARC?

在xcode 4.2中使用自动引用计数(arc)时,如何转换(或创建)一个编译并正确运行的单例类?


和你本该做的完全一样:

1
2
3
4
5
6
7
8
9
10
+ (instancetype)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}


如果要根据需要创建其他实例,请执行以下操作:

1
2
3
4
5
6
7
8
9
10
+ (MyClass *)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

否则,您应该这样做:

1
2
3
4
5
6
7
8
9
+ (id)allocWithZone:(NSZone *)zone
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}


这是ARC和非ARC的版本

如何使用:

mysingletonclass.h系列

1
2
3
4
5
@interface MySingletonClass : NSObject

+(MySingletonClass *)sharedInstance;

@end

mysingletonclass.m系列

1
2
3
4
5
#import"MySingletonClass.h"
#import"SynthesizeSingleton.h"
@implementation MySingletonClass
SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass)
@end

读这个答案,然后去读另一个答案。

首先,你必须知道单例是什么意思,它的要求是什么,如果你不理解它,那么你就不会理解这个解决方案——一点也不理解!

要成功创建singleton,您必须能够执行以下3项操作:

  • 如果存在竞争条件,那么我们不能允许同时创建多个sharedInstance实例!
  • 记住并保持多个调用之间的值。
  • 只创建一次。通过控制入口点。

dispatch_once_t只允许发送一次其块,从而帮助您解决争用条件。

EDOCX1[1]帮助您"记住"它在任何数量的调用。它是怎么记得的?它不允许再次创建具有sharedInstance的确切名称的任何新实例。它只与最初创建的实例一起使用。

不使用调用allocinit的方法(即,由于我们是nsObject子类,虽然不应该使用它们,但仍然有allocinit方法),我们在sharedInstance类上使用+(instancetype)sharedInstance来实现这一点,它被限定为只启动一次,而不考虑在同一个线程上来自不同线程的多次尝试。输入法并记住它的值。

可可本身带来的一些最常见的单子系统是:

  • [UIApplication sharedApplication]
  • [NSUserDefaults standardUserDefaults]
  • [NSFileManager defaultManager]
  • [NSBundle mainBundle]
  • [NSOperations mainQueue]
  • [NSNotificationCenter defaultCenter]

基本上,任何需要有集中效果的东西都需要遵循某种单例设计模式。


这是我在弧线下的图案。使用GCD满足新模式,也满足苹果的旧实例化预防模式。

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
@implementation AAA
+ (id)alloc
{
    return  [self allocWithZone:nil];
}
+ (id)allocWithZone:(NSZone *)zone
{
    [self doesNotRecognizeSelector:_cmd];
    abort();
}
+ (instancetype)theController
{
    static AAA* c1  =   nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        c1  =   [[super allocWithZone:nil] init];

        // For confirm...      
        NSLog(@"%@", NSStringFromClass([c1 class]));    //  Prints AAA
        NSLog(@"%@", @([c1 class] == self));            //  Prints 1

        Class   real_superclass_obj =   class_getSuperclass(self);
        NSLog(@"%@", @(real_superclass_obj == self));   //  Prints 0
    });

    return  c1;
}
@end


或者,Objective-C为nsObject及其所有子类提供了+(void)初始化方法。它总是在类的任何方法之前调用。

我在iOS6中设置了一次断点,在堆栈帧中出现了一次分派。


接受的答案有两个问题,可能与您的目的有关,也可能与您的目的无关。

  • 如果从init方法调用sharedInstance方法(例如,因为其他对象是从使用singleton的方法构造的),它将导致堆栈溢出。
  • 对于类层次结构,只有一个单例(即:调用sharedInstance方法的层次结构中的第一个类),而不是层次结构中每个具体类都有一个单例。
  • 以下代码处理这两个问题:

    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
    + (instancetype)sharedInstance {
        static id mutex = nil;
        static NSMutableDictionary *instances = nil;

        //Initialize the mutex and instances dictionary in a thread safe manner
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            mutex = [NSObject new];
            instances = [NSMutableDictionary new];
        });

        id instance = nil;

        //Now synchronize on the mutex
        //Note: do not synchronize on self, since self may differ depending on which class this method is called on
        @synchronized(mutex) {
            id <NSCopying> key = (id <NSCopying>)self;
            instance = instances[key];
            if (instance == nil) {
                //Break allocation and initialization into two statements to prevent a stack overflow, if init somehow calls the sharedInstance method
                id allocatedInstance = [self alloc];

                //Store the instance into the dictionary, one per concrete class (class acts as key for the dictionary)
                //Do this right after allocation to avoid the stackoverflow problem
                if (allocatedInstance != nil) {
                    instances[key] = allocatedInstance;
                }
                instance = [allocatedInstance init];

                //Following code may be overly cautious
                if (instance != allocatedInstance) {
                    //Somehow the init method did not return the same instance as the alloc method
                    if (instance == nil) {
                        //If init returns nil: immediately remove the instance again
                        [instances removeObjectForKey:key];
                    } else {
                        //Else: put the instance in the dictionary instead of the allocatedInstance
                        instances[key] = instance;
                    }
                }
            }
        }
        return instance;
    }

    singleton类:在任何情况下或通过任何方式,都不能创建类的多个对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    + (instancetype)sharedInstance
    {
        static ClassName *sharedInstance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedInstance = [[ClassName alloc] init];
            // Perform other initialisation...
        });
        return sharedInstance;
    }
    //    You need need to override init method as well, because developer can call [[MyClass alloc]init] method also. that time also we have to return sharedInstance only.

    -(MyClass)init
    {
       return [ClassName sharedInstance];
    }


    如果你需要用swift创建singleton,

    1
    2
    3
    4
    5
    6
    class var sharedInstance: MyClass {
        struct Singleton {
            static let instance = MyClass()
        }
        return Singleton.instance
    }

    1
    2
    3
    4
    5
    6
    7
    struct Singleton {
        static let sharedInstance = MyClass()
    }

    class var sharedInstance: MyClass {
        return Singleton.sharedInstance
    }

    你可以用这种方式

    1
    let sharedClass = LibraryAPI.sharedInstance

    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
    #import <Foundation/Foundation.h>

    @interface SingleTon : NSObject

    @property (nonatomic,strong) NSString *name;
    +(SingleTon *) theSingleTon;

    @end

    #import"SingleTon.h"
    @implementation SingleTon

    +(SingleTon *) theSingleTon{
        static SingleTon *theSingleTon = nil;

        if (!theSingleTon) {

            theSingleTon = [[super allocWithZone:nil] init
                         ];
        }
        return theSingleTon;
    }

    +(id)allocWithZone:(struct _NSZone *)zone{

        return [self theSingleTon];
    }

    -(id)init{

        self = [super init];
        if (self) {
            // Set Variables
            _name = @"Kiran";
        }

        return self;
    }

    @end

    希望上面的代码能帮上忙。