Ви запитуєте, чи це "найкращий спосіб створити одиночку".
По-перше, так, це безпечне для ниток рішення. Ця 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
Тоді ви можете продовжити і написати Getter для цієї властивості (реалізація використовуватиме 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
є розсудливим.