Você pergunta se esta é a "melhor maneira de criar singleton".
Primeiro, sim, esta é uma solução segura para threads. Esse dispatch_once
padrão é a maneira moderna e segura de thread de gerar singletons no Objective-C. Não se preocupe lá.
Você perguntou, porém, se essa é a "melhor" maneira de fazer isso. Deve-se reconhecer, porém, que o instancetype
e [[self alloc] init]
é potencialmente enganoso quando usado em conjunto com singletons.
O benefício instancetype
disso é que é uma maneira inequívoca de declarar que a classe pode ser subclassificada sem recorrer a um tipo de id
, como tivemos que fazer no passado.
Mas o static
método apresenta desafios de subclassificação. E se ImageCache
e BlobCache
singletons fossem subclasses de uma Cache
superclasse sem implementar seu próprio sharedCache
método?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
Para que isso funcione, você deve garantir que as subclasses implementem seu próprio sharedInstance
método (ou o que você chamar para sua classe específica).
Resumindo, seu original sharedInstance
parece suportar subclasses, mas não. Se você pretende dar suporte à subclasse, inclua pelo menos a documentação que avisa os desenvolvedores futuros que eles devem substituir esse método.
Para melhor interoperabilidade com o Swift, você provavelmente deseja definir isso como uma propriedade, não um método de classe, por exemplo:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
Em seguida, você pode prosseguir e escrever um getter para essa propriedade (a implementação usaria o dispatch_once
padrão sugerido):
+ (Foo *)sharedFoo { ... }
O benefício disso é que, se um usuário Swift for usá-lo, ele fará algo como:
let foo = Foo.shared
Observe que não existe ()
, porque nós o implementamos como uma propriedade. A partir do Swift 3, é assim que os singletons são geralmente acessados. Portanto, defini-lo como uma propriedade ajuda a facilitar essa interoperabilidade.
Como um aparte, se você observar como a Apple está definindo seus singletons, esse é o padrão que eles adotaram, por exemplo, seu NSURLSession
singleton é definido da seguinte maneira:
@property (class, readonly, strong) NSURLSession *sharedSession;
Outra consideração muito pequena sobre a interoperabilidade Swift foi o nome do singleton. É melhor se você pode incorporar o nome do tipo em vez de sharedInstance
. Por exemplo, se a classe era Foo
, você pode definir a propriedade singleton como sharedFoo
. Ou, se a classe fosse DatabaseManager
, você poderia chamar a propriedade sharedManager
. Os usuários do Swift poderiam fazer:
let foo = Foo.shared
let manager = DatabaseManager.shared
Claramente, se você realmente deseja usar sharedInstance
, sempre pode declarar o nome Swift, se desejar:
@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
Claramente, ao escrever o código Objective-C, não devemos permitir que a interoperabilidade Swift supere outras considerações de design, mas ainda assim, se pudermos escrever código que suporte graciosamente os dois idiomas, é preferível.
Concordo com os outros que apontam que, se você quer que isso seja um verdadeiro singleton onde os desenvolvedores não pode / não deve (acidentalmente) instanciar suas próprias instâncias, o unavailable
qualificador no init
e new
é prudente.