[英]How to define a `Class` object type conforming to a protocol?
考慮以下 Objective-C 協議聲明,它只需要 class 方法:
@protocol TDWMethoding<NSObject>
+ (void)foo;
+ (void)bar;
@end
假設我需要從一個方法返回一個符合這個協議的Class
的實例,我應該如何指定返回類型?
- (nullable /*return-type*/)instantiateMethoding {
Class instance = ... // some implementation
if ([instance conformsToProtocol:@protocol(TDWMethoding)]) {
return instance;
}
return nil;
}
到目前為止,我考慮了很多關於如何表達/*return-type*/
的工作選項,但每個選項都有其自身的缺點:
Class
- 這樣它就不會暴露一致性。 Class
是個什么樣的? 它有什么作用? 它完全符合協議嗎?Class<TDWMethoding>
- 這看起來像是一個可行的解決方案,甚至被其他開發人員(這里和這里)建議過幾次,但我個人認為它不一致且具有誤導性:當我們有一個Type<Protocol> *instance
形式的變量時,它通常意味着協議 class 方法應該發送到實例的 class ( [[instance class] foo]
) 而不是實例本身 ( [instance foo]
);id<TDWMethoding>
並返回 class 的實例 - 這是一致的,但它需要我實例化 class,這既是多余的,又阻止我隱藏符合NS_UNAVAILABLE
宏協議的實用程序類的構造函數。是否有更好的語義來表達這種返回類型?
Class<TDWMethoding>
是正確的。 這並不矛盾。 當某物的類型為Class
時,您向其發送 class 方法。 當某物是一個實例,並且您想發送到 class 時,您可以訪問它的-class
。
也就是說,這看起來確實很奇怪,並且可能意味着您過度使用了 Class 方法。 為此,您應該認真考慮sharedInstance
是否更好 model。
但是,如果您想識別類型, Class<TDWMethoding>
是正確的,盡管id
可能更常見,如如何將 Class object 轉換為符合協議中所述。
在深入了解Objective-C Programming Language 文檔后,我實際上找到了這種情況的確切答案:
協議不能用於鍵入 class 對象。 只有實例可以靜態類型化為協議,就像只有實例可以靜態類型化為 class 一樣。(但是,在運行時,類和實例都會響應 conformsToProtocol
conformsToProtocol:
消息。)
這意味着它不受支持,我應該以不同的方式實現它。 (例如使用 singleton 模式,如Rob 的回答中所建議)
解決方案是根本不使用此類協議。 為什么? 因為它不靈活。
它應該只是:
@protocol TDWMethoding
- (void)foo;
- (void)bar;
@end
然后你將能夠做任何你想做的事,例如你將能夠為你的 class 創建包裝器,這將實現你的協議。
@interface TDWMethodingModel<TDWMethoding>
@property (nonatomic, readonly) void (^fooCaller)(void);
@property (nonatomic, readonly) void (^barCaller)(void);
- (instancetype)initWithFooCaller:(void (^)(void))fooCaller barCaller:(void (^)(void))barCaller NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
@implementation TDWMethodingModel
- (instancetype)initWithFooCaller:(void (^)(void))fooCaller barCaller:(void (^)(void))barCaller {
self = [super init];
if (nil == self) {
return nil;
}
_fooCaller = fooCaller;
_barCaller = barCaller;
return self;
}
- (void)foo {
self.fooCaller();
}
- (void)bar {
self.barCaller();
}
@end
然后:
- (id<TDWMethoding>)instantiateMethoding
{
static id<TDWMethoding> methoding;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
methoding = [[TDWMethodingModel alloc] initWithFooCaller:^{
[SomeClass foo];
} barCaller:^{
[SomeClass bar];
}];
});
return methoding;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.