Bạn hỏi liệu đây có phải là "cách tốt nhất để tạo singleton" không.
Đầu tiên, vâng, đây là một giải pháp an toàn chủ đề. Đây dispatch_once
mô hình là thread-an toàn, cách hiện đại để tạo ra độc thân trong Objective-C. Không phải lo lắng ở đó.
Tuy nhiên, bạn đã hỏi liệu đây có phải là cách "tốt nhất" để làm điều đó không. Tuy nhiên, người ta phải thừa nhận rằng instancetype
và [[self alloc] init]
có khả năng gây hiểu lầm khi được sử dụng cùng với singletons.
Lợi ích của instancetype
việc đó là một cách rõ ràng để tuyên bố rằng lớp có thể được phân lớp mà không cần dùng đến một loại id
, giống như chúng ta phải làm trong năm qua.
Nhưng static
trong phương pháp này đưa ra những thách thức phân lớp. Điều gì xảy ra nếu ImageCache
và BlobCache
singletons đều là các lớp con từ một Cache
siêu lớp mà không thực hiện sharedCache
phương thức riêng của chúng ?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
Để làm việc này, bạn phải đảm bảo các lớp con thực hiện phương thức riêng của chúng sharedInstance
(hoặc bất cứ điều gì bạn gọi nó cho lớp cụ thể của bạn).
Tóm lại, bản gốc của bạn có sharedInstance
vẻ như sẽ hỗ trợ các lớp con, nhưng nó sẽ không. Nếu bạn có ý định hỗ trợ phân lớp, ít nhất bao gồm tài liệu cảnh báo các nhà phát triển trong tương lai rằng họ phải ghi đè phương thức này.
Để có khả năng tương tác tốt nhất với Swift, có lẽ bạn muốn xác định đây là một thuộc tính, không phải là một phương thức lớp, ví dụ:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
Sau đó, bạn có thể tiếp tục và viết một getter cho thuộc tính này (việc triển khai sẽ sử dụng dispatch_once
mẫu mà bạn đề xuất):
+ (Foo *)sharedFoo { ... }
Lợi ích của việc này là nếu người dùng Swift sử dụng nó, họ sẽ làm một cái gì đó như:
let foo = Foo.shared
Lưu ý, không có ()
, bởi vì chúng tôi đã triển khai nó như một tài sản. Bắt đầu Swift 3, đây là cách các singletons thường được truy cập. Vì vậy, xác định nó là một tài sản giúp tạo điều kiện cho khả năng tương tác đó.
Ở một khía cạnh khác, nếu bạn nhìn vào cách Apple định nghĩa các singletons của họ, thì đây là mô hình mà họ đã áp dụng, ví dụ như NSURLSession
singleton của họ được định nghĩa như sau:
@property (class, readonly, strong) NSURLSession *sharedSession;
Một xem xét khả năng tương tác Swift rất nhỏ khác là tên của singleton. Tốt nhất là bạn có thể kết hợp tên của loại chứ không phải sharedInstance
. Ví dụ: nếu lớp là Foo
, bạn có thể định nghĩa thuộc tính singleton là sharedFoo
. Hoặc nếu lớp là DatabaseManager
, bạn có thể gọi tài sản sharedManager
. Sau đó, người dùng Swift có thể làm:
let foo = Foo.shared
let manager = DatabaseManager.shared
Rõ ràng, nếu bạn thực sự muốn sử dụng sharedInstance
, bạn luôn có thể khai báo tên Swift nếu bạn muốn:
@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
Rõ ràng, khi viết mã Objective-C, chúng ta không nên để khả năng tương tác của Swift vượt xa các cân nhắc thiết kế khác, nhưng vẫn có thể, nếu chúng ta có thể viết mã hỗ trợ duyên dáng cho cả hai ngôn ngữ, điều đó tốt hơn.
Tôi đồng ý với những người khác chỉ ra rằng nếu bạn muốn đây là một người độc thân thực sự nơi các nhà phát triển không thể / không nên (vô tình) khởi tạo các thể hiện của riêng họ, thì unavailable
vòng loại trên init
và new
thận trọng.