IOS 4.0以降をターゲットにできる場合
GCDを使用して、Objective Cでシングルトンを作成する最良の方法ですか?
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
これは、クラスのインスタンスを作成するための完全に受け入れられ、スレッドセーフな方法です。技術的には「シングルトン」ではない場合があります(これらのオブジェクトは1つしか存在できないため)が、[Foo sharedFoo]
メソッドを使用してオブジェクトにアクセスする限り、これで十分です。
instancetype
は、Objective-C
の多くの言語拡張機能の1つに過ぎず、新しいリリースごとにさらに追加されます。
知って、愛して。
また、低レベルの詳細に注意を払うと、Objective-Cを変革するための強力な新しい方法への洞察がどのように得られるかを示す例として考えてください。
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^
{
sharedInstance = [self new];
});
return sharedInstance;
}
+ (Class*)sharedInstance
{
static dispatch_once_t once;
static Class *sharedInstance;
dispatch_once(&once, ^
{
sharedInstance = [self new];
});
return sharedInstance;
}
MySingleton.h
@interface MySingleton : NSObject
+(instancetype)sharedInstance;
+(instancetype)alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype)init __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype)new __attribute__((unavailable("new not available, call sharedInstance instead")));
-(instancetype)copy __attribute__((unavailable("copy not available, call sharedInstance instead")));
@end
MySingleton.m
@implementation MySingleton
+(instancetype)sharedInstance {
static dispatch_once_t pred;
static id shared = nil;
dispatch_once(&pred, ^{
shared = [[super alloc] initUniqueInstance];
});
return shared;
}
-(instancetype)initUniqueInstance {
return [super init];
}
@end
Allocメソッドを上書きして、クラスが割り当てられるのを避けることができます。
@implementation MyClass
static BOOL useinside = NO;
static id _sharedObject = nil;
+(id) alloc {
if (!useinside) {
@throw [NSException exceptionWithName:@"Singleton Vialotaion" reason:@"You are violating the singleton class usage. Please call +sharedInstance method" userInfo:nil];
}
else {
return [super alloc];
}
}
+(id)sharedInstance
{
static dispatch_once_t p = 0;
dispatch_once(&p, ^{
useinside = YES;
_sharedObject = [[MyClass alloc] init];
useinside = NO;
});
// returns the same object each time
return _sharedObject;
}
デイブは正しいです、それはまったく問題ありません。 シングルトンの作成に関するAppleのドキュメント 他のメソッドを実装するためのヒントについては、クラスがsharedFooメソッドを使用しないことを選択した場合に1つのみを作成できるようにすることをお勧めします。
[[MyClass alloc] init]がsharedInstanceと同じオブジェクトを返すようにしたい場合(私の意見では必要ではありませんが、それを望む人もいます)、2番目のdispatch_onceを使用して非常に簡単かつ安全に実行できます:
- (instancetype)init
{
static dispatch_once_t once;
static Class *sharedInstance;
dispatch_once(&once, ^
{
// Your normal init code goes here.
sharedInstance = self;
});
return sharedInstance;
}
これにより、[[MyClass alloc] init]と[MyClass sharedInstance]の任意の組み合わせで同じオブジェクトを返すことができます。 [MyClass sharedInstance]は、もう少し効率的です。仕組み:[MyClass sharedInstance]は[[MyClass alloc] init]を1回呼び出します。他のコードでも何度でも呼び出すことができます。 initの最初の呼び出し元は、「通常の」初期化を行い、シングルトンオブジェクトをinitメソッドに格納します。後でinitを呼び出すと、allocが返したものは完全に無視され、同じsharedInstanceが返されます。 allocの結果は割り当て解除されます。
+ sharedInstanceメソッドは、常に機能します。 [[MyClass alloc] init]を呼び出す最初の呼び出し元でない場合、initの結果はalloc呼び出しの結果ではありませんが、それで問題ありません。
これが「シングルトンを作成する最良の方法」かどうかを尋ねます。
いくつかの考え:
まず、はい、これはスレッドセーフなソリューションです。このdispatch_once
パターンは、Objective-Cでシングルトンを生成するための最新のスレッドセーフな方法です。心配ありません。
しかし、あなたはこれがそれを行うための「最良の」方法であるかどうか尋ねました。ただし、instancetype
および[[self alloc] init]
はシングルトンと組み合わせて使用すると誤解を招く可能性があることを認識する必要があります。
instancetype
の利点は、昨年のようにid
の型に頼らずにクラスをサブクラス化できることを明確に宣言できることです。
ただし、このメソッドのstatic
にはサブクラス化の課題があります。 ImageCache
およびBlobCache
シングルトンが、独自のCache
メソッドを実装せずに、両方ともsharedCache
スーパークラスのサブクラスであった場合はどうなりますか?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
これが機能するためには、サブクラスが独自のsharedInstance
(または特定のクラスに対して呼び出すもの)メソッドを実装することを確認する必要があります。
要するに、元のsharedInstance
looksはサブクラスをサポートしますが、サポートしません。サブクラス化をサポートする場合は、少なくともこのメソッドをオーバーライドする必要があることを将来の開発者に警告するドキュメントを含めてください。
Swiftとの相互運用性を最大限に高めるには、おそらく、これをクラスメソッドではなくプロパティとして定義する必要があります。
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
次に、このプロパティのゲッターを作成します(実装では、提案したdispatch_once
パターンを使用します)。
+ (Foo *)sharedFoo { ... }
この利点は、Swiftユーザーがそれを使用する場合、次のようなことをすることです。
let foo = Foo.shared
プロパティとして実装したため、()
はありません。 Swift 3以降、これがシングルトンへの一般的なアクセス方法です。したがって、プロパティとして定義すると、その相互運用性が促進されます。
余談ですが、Appleがシングルトンをどのように定義しているかを見ると、これが彼らが採用しているパターンです。それらのNSURLSession
シングルトンは、次のように定義されます。
@property (class, readonly, strong) NSURLSession *sharedSession;
もう1つの非常に小さなSwiftの相互運用性に関する考慮事項は、シングルトンの名前でした。 sharedInstance
ではなく、タイプの名前を組み込むことができれば最適です。たとえば、クラスがFoo
の場合、シングルトンプロパティをsharedFoo
として定義できます。または、クラスがDatabaseManager
の場合、プロパティsharedManager
を呼び出すことができます。その後、Swiftユーザーは次のことができます。
let foo = Foo.shared
let manager = DatabaseManager.shared
明らかに、sharedInstance
を本当に使用したい場合は、いつでもSwift名を宣言できます。
@property (class, readonly, strong) Foo* sharedInstance NS_Swift_NAME(shared);
明らかに、Objective-Cコードを作成するとき、Swiftの相互運用性が他の設計上の考慮事項を上回るべきではありませんが、それでも、両方の言語を適切にサポートするコードを作成できる場合は望ましいです。
開発者が自分のインスタンスを(誤って)インスタンス化できない/すべきでない真のシングルトンにしたい場合は、unavailable
およびinit
のnew
修飾子が賢明であると指摘する他の人に同意します。
スレッドセーフシングルトンを作成するには、次のようにします。
@interface SomeManager : NSObject
+ (id)sharedManager;
@end
/* thread safe */
@implementation SomeManager
static id sharedManager = nil;
+ (void)initialize {
if (self == [SomeManager class]) {
sharedManager = [[self alloc] init];
}
}
+ (id)sharedManager {
return sharedManager;
}
@end
このブログではシングルトンについて非常によく説明しています objc/cocoaのシングルトン
//Create Singleton
+( instancetype )defaultDBManager
{
static dispatch_once_t onceToken = 0;
__strong static id _sharedObject = nil;
dispatch_once(&onceToken, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
//In it method
-(instancetype)init
{
self = [super init];
if(self)
{
//Do your custom initialization
}
return self;
}