如何实现与 ARC 兼容的 Objective-C 单例?

在 Xcode 4.2中使用自动引用计数(ARC)时,如何转换(或创建)一个能够正确编译和执行的单例类?

96445 次浏览

就像你(应该)已经在做的那样:

+ (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;
}

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

+ (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;
}

否则,你应该这样做:

+ (id)allocWithZone:(NSZone *)zone
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}

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

我在 iOS6中设置了一个断点,然后在堆栈框架中出现了 send _ once。

这是我在 ARC 下的模式。 使用 GCD 满足新模式,同时也满足苹果公司旧的实例化防范模式。

@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
#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

希望以上代码可以帮助解决这个问题。

如果你需要在 Swift 中创建单例模式,

class var sharedInstance: MyClass {
struct Singleton {
static let instance = MyClass()
}
return Singleton.instance
}

或者

struct Singleton {
static let sharedInstance = MyClass()
}


class var sharedInstance: MyClass {
return Singleton.sharedInstance
}

你可以用这种方式

let sharedClass = LibraryAPI.sharedInstance

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

首先,您必须知道 Singleton 是什么意思以及它的需求是什么,如果您不理解它,那么您将根本不会理解解决方案!

要成功地创建一个 Singleton,你必须能够做到以下3点:

  • 如果有一个 比赛状态,那么我们不能允许同时创建您的 SharedInstance 的多个实例!
  • 记住并保留多个调用之间的值。
  • 只创建一次。通过控制入口点。

dispatch_once_t帮助您解决一个 比赛状态只允许其块被派遣一次。

Static 帮助您“记住”任意数量的 祈祷。它怎么会记得?它不允许再次创建任何具有您的 sharedInstance 确切名称的新实例,它只与最初创建的实例一起工作。

在我们的 sharedInstance 类上不使用 调用 alloc init(即我们仍然有 alloc init方法,因为我们是 NSObject 子类,尽管我们不应该使用它们) ,我们通过使用 +(instancetype)sharedInstance来实现这一点,它被限定为只能是 启动过一次,不管同时来自不同线程的多次尝试,并记住它的值。

Cocoa 自带的一些最常见的系统 Singleton 是:

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

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

单例类: 任何人都不能在任何情况下或通过任何方式创建一个以上的类对象。

+ (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];
}

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

  1. 如果从 init 方法再次调用 sharedInstance 方法(例如,因为其他对象是从那里构造的,它们使用单例) ,它将导致堆栈溢出。
  2. 对于类层次结构,只有一个单例(即: 调用 sharedInstance 方法的层次结构中的第一个类) ,而不是层次结构中每个具体类只有一个单例。

下面的代码解决了这两个问题:

+ (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;
}