帮助理解返回单例的类方法

有人可以帮我理解下面的方法是做什么的吗?

+ (Game *) shared { static Game *sharedSingleton; @synchronized(self) { if (!sharedSingleton) { sharedSingleton = [[Game alloc] init]; } } return sharedSingleton; } 

显然,单例背后的想法是只创建一个实例。 实现这一目标的第一步是通过行static Game *sharedSingleton;声明类的静态实例static Game *sharedSingleton;

第二步是检查单个实例是否已经创建,如果不是,则创建它,或者如果是,则返回现有的单个实例。 但是,如果2个单独的线程尝试在同一时刻调用+shared方法,则第二步可能会出现问题。 您不希望一个线程修改单个sharedSingleton变量,而另一个线程正在尝试检查它,因为它可能会产生意外结果。

此问题的解决方案是使用@synchronized()编译器指令来同步对括号之间指定的对象的访问。 例如,假设Game类的这个单独共享实例有一个名为players的实例变量,它是一个Player类实例的NSMutableArray 。 假设Game类有一个-addPlayer:方法,它可以通过添加指定的播放器来修改players实例变量。 重要的是,如果从多个线程调用该方法,则一次只允许一个线程修改playersarrays。 因此,该方法的实现可能如下所示:

 - (void)addPlayer:(Player *)player { if (player == nil) return; @synchronized(players) { [players addObject:player]; } } 

使用@synchronized()指令可确保一次只有一个线程可以访问@synchronized()变量。 如果一个线程在另一个线程当前正在访问它时尝试,则第一个线程必须等到另一个线程完成。

虽然在谈论实例变量时它更直接,但是如何在类本身的单个创建方法中实现相同类型的结果可能不太清楚。 以下代码中@synchronized(self)行中的@synchronized(self)基本上等同于Game类本身。 通过在Game类上同步,它确保sharedSingleton = [[Game alloc] init]; line只被调用一次。

 + (Game *) shared { static Game *sharedSingleton; @synchronized(self) // assures only one thread can call [Game shared] at a time { if (!sharedSingleton) { sharedSingleton = [[Game alloc] init]; } } return sharedSingleton; } 

[编辑]:更新。 基于我的测试一段时间(我现在只是重新测试),以下所有看起来都是等效的:

@implementation之外:

 Game *sharedInstance; @implementation Game + (Game *)sharedGame { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[[self class] alloc] init]; } } return sharedInstance; } @end 

@implementation之外, static

 static Game *sharedInstance; @implementation Game + (Game *)sharedGame { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[[self class] alloc] init]; } } return sharedInstance; } @end 

里面@implementation

 @implementation Game static Game *sharedInstance; + (Game *)sharedGame { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[[self class] alloc] init]; } } return sharedInstance; } @end 

内部+sharedGame

 @implementation Game + (Game *)sharedGame { static Game *sharedInstance; @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[[self class] alloc] init]; } } return sharedInstance; } @end 

唯一的区别是,在第一个变体中,没有static关键字, sharedInstance不会出现在gdb File Statics下。 显然,在上一个变体中, sharedInstance+sharedGame方法之外是不可见的。 但实际上,他们都确保当你调用[Game sharedInstance]你将获得sharedInstance ,并且sharedInstance只创建一次。 (但是,请注意,需要采取进一步的预防措施,以防止某人使用Game *game = [[Game alloc] init]; )等创建非单例实例。

逐行解释……

 // A static variable guarantees there's only 1 instance of it ever, // even accross multiple instances of the same class, this particular // variable will store the class instance, so it can be returned whenever // a client-class requests an instance of this class. static Game *sharedSingleton; // create a method that can always be called, even if there's no instance yet // this method should create a new instance if there isn't one yet, otherwise // return the existing instance + (Game *) shared { // synchronized makes sure only 1 client class can enter this method at any time, // eg to prevent creating 2 instances whenever 2 client-classes try to // access the following code from different threads. @synchronized(self) { // if the static variable is called for the first time, // create an instance and return it, otherwise return the existing instance ... if (!sharedSingleton) { sharedSingleton = [[Game alloc] init]; } } return sharedSingleton; } 

我不太了解Objective-C,但它似乎是一种获取Game *类型的单例副本的方法,并且是为了线程安全 。

基本上调用该方法将每次返回相同的Game *副本(不管是哪个线程),并且在第一次调用它之前不会分配新的Game实例(称为延迟加载 )

我还会覆盖’mutableCopyWithZone:’和’copyWithZone:’方法,以避免单例被复制:

 // copy cannot be done - (Game *)copyWithZone:(NSZone *)zone { return self; } // mutablecopy cannot be done - (Game *)mutableCopyWithZone:(NSZone *)zone { return [self copyWithZone:zone]; } 

由于’copy’和’mutableCopy’(假设单例类不从另一个已经覆盖默认NSObject实现的类inheritance),只需调用’copyWithZone:’和’mutableCopyWithZone:’,也不需要重写它们。

为了避免其他开发人员使用’init’或’new’打破单例模式,可以将这两个方法声明为不可用:

 - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; 

通过这种方式,编译器将不允许使用这两种方法,并且其他开发人员将被迫使用记录的方法来获取共享实例。 另一个更严格的技术是覆盖’init’和’new’并引发exception。