有人可以帮我理解以下方法在做什么吗?
+ (Game *) shared
{
static Game *sharedSingleton;
@synchronized(self)
{
if (!sharedSingleton)
{
sharedSingleton = [[Game alloc] init];
}
}
return sharedSingleton;
}
有人可以帮我理解以下方法在做什么吗?
+ (Game *) shared
{
static Game *sharedSingleton;
@synchronized(self)
{
if (!sharedSingleton)
{
sharedSingleton = [[Game alloc] init];
}
}
return sharedSingleton;
}
显然,单例背后的想法是只创建一个实例。实现这一点的第一步是通过行声明类的静态实例static Game *sharedSingleton;
。
第二步是检查单个实例是否已经创建,如果没有,则创建它,如果是,则返回现有的单个实例。+shared
但是,如果 2 个单独的线程试图在同一时刻调用该方法,则第二步可能会出现问题。您不希望一个线程sharedSingleton
在另一个线程尝试检查它时修改单个变量,因为它可能会产生意想不到的结果。
解决这个问题的方法是使用@synchronized()
编译器指令来同步访问括号之间指定的对象。例如,假设这个Game
类的单个共享实例有一个名为的实例变量players
,它是一个类NSMutableArray
的实例Player
。假设Game
该类有一个-addPlayer:
方法,该方法将players
通过添加指定的播放器来修改实例变量。重要的是,如果从多个线程调用该方法,则一次只允许一个线程修改players
数组。因此,该方法的实现可能如下所示:
- (void)addPlayer:(Player *)player {
if (player == nil) return;
@synchronized(players) {
[players addObject:player];
}
}
使用该@synchronized()
指令可确保一次只有一个线程可以访问该players
变量。如果一个线程在另一个线程当前正在访问它时尝试访问它,则第一个线程必须等到另一个线程完成。
虽然当您谈论实例变量时更直接,但可能不太清楚如何在类本身的单一创建方法中实现相同类型的结果。以下代码self
中的@synchronized(self)
in 基本上等同于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;
}
[编辑]:更新。根据我不久前的测试(我现在刚刚重新测试),以下所有内容似乎都是等效的:
外部@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
. 显然,在最后一个变体中,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,
// e.g. 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;
}
我还将覆盖 '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 实现的类)只需调用 'copyWithZone:' 和 'mutableCopyWithZone:' 就不需要覆盖它们了。
为了避免其他开发人员通过使用 'init' 或 'new' 来破坏单例模式,可以将这两种方法声明为不可用:
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
这样编译器将不允许使用这两种方法,并且其他开发人员将被迫使用记录在案的方法来获取共享实例。另一种更严格的技术是覆盖“init”和“new”并引发异常。