+ (Game *) shared { static Game *sharedSingleton; @synchronized(self) { if (!sharedSingleton) { sharedSingleton = [[Game alloc] init]; } } return sharedSingleton; }
解决方法
第二步是检查单个实例是否已经创建,如果不是,创建它,或者如果是,则返回现有的单个实例.但是,第二个步骤在两个单独的线程尝试在同一个确切时刻调用共享方法的情况下,开启了潜在的问题.您不会希望一个线程修改单个sharedSingleton变量,而另一个线程正在尝试检查它,因为它可能会产生意想不到的结果.
解决此问题的方法是使用@synchronized()编译器指令来同步对括号之间指定的对象的访问.例如,说这个Game类的这个单独的共享实例有一个名为players的实例变量,它是一个Player类的实例的NSMutableArray.让我们说Game类有一个-addPlayer:方法,它通过添加指定的播放器来修改player实例变量.重要的是,如果从多个线程调用该方法,则只允许一个线程一次修改player数组.因此,该方法的实现可能如下所示:
- (void)addPlayer:(Player *)player { if (player == nil) return; @synchronized(players) { [players addObject:player]; } }
使用@synchronized()指令确保只有一个线程可以一次访问player变量.如果一个线程尝试在另一个线程正在访问它时,第一个线程必须等到另一个线程完成.
虽然在谈论实例变量时更直接,但是在类本身的单一创建方法中如何实现相同类型的结果也许不太清楚.以下代码中的@synchronized(self)行中的self基本上等同于Game类本身.通过在Game类上同步,它保证sharedSingleton = [[Game alloc] init];线只有一次被叫过.
+ (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; }
[编辑]:更新.根据我的测试一下(现在我刚刚重新测试),以下所有似乎都是等效的:
外部实现:
Game *sharedInstance; @implementation Game + (Game *)sharedGame { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[[self class] alloc] init]; } } return sharedInstance; } @end
外部@implementation,静态:
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
内共享游戏
@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];)来创建非单例实例.