[英]Objective-C - Effective Subclassing of Cocoa Class Clusters
我有一個對象,曾經是一個NSMutableSet,但需要更多的東西附加到它。 顯而易見(顯然不受支持)的事情是將NSMutableSet子類化並添加兩個額外的屬性。 由於NSMutableSet與基本上所有的Cocoa數據結構一樣,是一個類集群,我不能以通常的方式對它進行子類化,因為超類只拋出異常。 這導致我走了幾條路。
第一個路徑是創建一個復合對象,它聲明自己是NSMutableSet的子類,但實際上只是將調用轉發到內部NSMutableSet。 我不想在NSMutableSet上實現每個方法,所以我認為forwardInvocation:
將是完成我的任務的好方法。 不幸的是,NSMutableSet的抽象類實現了接口上的所有方法,並且它們的實現拋出異常,所以我從來沒有達到可以轉發調用的程度。
第二條路徑是NSProxy的子類,並從那里轉發調用。 這個解決方案不足以說我需要復制NSMutableSet的接口,除非有辦法聲明“這個類實現這個接口”,我不知道(這很可能是解決方案)。
第三條路徑是在NSMutableSet上創建一個類別,並僅為需要使用它的類導入它,但由於您無法通過類別添加非動態屬性,因此該類別不足。 這導致我在一個類別中使用關聯對象。 我願意承認這是這個用例的正確解決方案,但我希望它不是因為它有點笨重。 它是雙重笨重的,因為我添加的屬性是原始的,所以我必須在設置和獲取關聯時包裝和解包它們(除非有一種方法來關聯我不熟悉的基元)。
本質上,我想要的是在功能上作為NSMutableSet(和所有類集群)的子類行為,但無法找出最佳方法。 謝謝!
試圖繼承Cocoa類集群只會造成很大的傷害。 這似乎是一個好主意,但你會永遠遇到問題。
只需使用NSMutableSet作為第一個成員對象創建一個NSObject。
對Cocoa類集群進行子類化是有點氣餒的。 不無道理。 請不要進入這個崩潰的世界。
您的任何一種解決方案都可行。 我已成功使用NSArray
和NSDictionary
的第一個路徑,所以我相信它也適用於NSMutableSet
。 請記住,您不僅需要覆蓋forwardInvocation:
還需要覆蓋其他一些方法。 請參閱Apple文檔的Surrogate Objects部分:
雖然轉發模仿繼承,但NSObject類從不混淆這兩者。 像respondsToSelector:和isKindOfClass這樣的方法:只查看繼承層次結構,而不是轉發鏈。
就我而言,我已經覆蓋了:
conformsToProtocol:
isKindOfClass:
isMemberOfClass:
respondsToSelector:
instancesRespondToSelector:
forwardInvocation:
methodSignatureForSelector:
instanceMethodSignatureForSelector:
從isKindOfClass:
conformsToProtocol:
和respondsToSelector:
肯定是至關重要的。
我也使用了第三條路徑並取得了良好的效果,但我承認相關的對象API很笨拙。
首先,gnasher729是正確的。 不要對類集群進行子類化。 只是不要這樣做。 你可以做到嗎? 如果我告訴你你做不到,那會不會讓你說服你自己不應該? 如果能幫助你做出正確的選擇,我可以撒謊。
但嚴肅地說,它幾乎總是毫無意義。 是您真正的子類特定類型的一套? 或者它真的有點像一套。 考慮NSAttributedString
。 它不是一種字符串,它有一個字符串。 這幾乎總是更好。
而且,類集群恰好是子類的王室痛苦。
也就是說,正如您已經發現的那樣,將相關值添加到數據結構中通常很好,因為您真正想要的是“嘿,我有一些數據需要與其他數據一起使用”。 包裝變得如此簡單,以至於它不應該讓你失望。 請參閱https://stackoverflow.com/a/14918158/97337 :
objc_setAssociatedObject(self, animatingKey, @(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
通過“一個奇怪的技巧” ,你可以很容易地做到這一點:
@interface NSObject (BoolVal)
@property (nonatomic, readwrite, assign) BOOL boolVal;
@end
@implementation NSObject (BoolVal)
- (BOOL)boolVal {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setBoolVal:(BOOL)value {
objc_setAssociatedObject(self, @selector(boolVal), @(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
但我還是會回來的,這是否真的是一種集(而不是就像一組)的問題,以及是否真的需要每一個可以發送給一組消息作出響應。 與NSAttributedString
,您的實際需求通常比實際需求小得多,並且包含您需要的少數方法通常值得簡單和控制。
為了完整起見,讓我們看看你的第一條道路:
創建一個復合對象的類,它聲明自己是
NSMutableSet
的子類,但實際上只是將調用轉發到內部NSMutableSet
你可以子類化NSMutableSet
嗎? 是的,但是你呢? NSMutableSet
的文檔說:
子類注釋
應該沒有必要進行子類化。 如果您需要自定義行為,通常最好考慮組合而不是子類化。
所以權衡一下,如果你想要繼承,請再次參考文檔:
覆蓋的方法
在子類中,您必須覆蓋它們的兩個基本方法:
addObject:
removeObject:
您還必須覆蓋
NSSet
類的基本方法。
看一下NSSet
類文檔,我們發現它的原始方法是:
覆蓋的方法
在子類中,您必須覆蓋其所有基本方法:
count
member:
objectEnumerator
就是這樣,5種方法。
您可以將自己的類定義為NSMutableSet
的子類,添加一個實例變量,它是NSMutableSet
一個實例,實現5個方法並將它們重定向到set實例,添加您希望的任何init方法,然后添加其他屬性。
如果性能受到關注,則需要權衡重定向這五種方法和訪問相關對象以獲取其他屬性。 您需要進行配置才能解決這個問題,但是當且僅當性能成為問題時。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.