Вы спрашиваете, является ли это «лучшим способом создания синглтона».
Во-первых, да, это поточно-ориентированное решение. Этот 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
выглядит так, как будто он будет поддерживать подклассы, но это не так. Если вы намереваетесь поддерживать подклассы, по крайней мере, включите документацию, которая предупреждает будущих разработчиков, что они должны переопределить этот метод.
Для лучшей совместимости со 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;
Другим, очень незначительным соображением совместимости Swift было название синглтона. Лучше, если вы можете включить название типа, а не sharedInstance
. Например, если класс был Foo
, вы можете определить свойство singleton как 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
разумен.