2010-10-05 418 views
18

我有一個NSMutableArray對象,我想添加自定義方法。我嘗試了繼承NSMutableArray,但是當我嘗試使用count方法獲取對象的數量時,出現錯誤,指出「方法僅爲抽象類定義」。爲什麼count方法不被繼承?我應該繼承NSMutableArray類

我在其他地方讀過,如果我想使用它們,我必須將一些NSMutableArray方法導入到我的自定義類中。我只想將自定義方法添加到NSMutableArray類。那麼我應該子類化NSMutableArray,還是應該做其他的事情?

回答

32

NSMutableArray不是一個具體的類,它只是類集羣的抽象超類NSMutableArray的文檔確實有關於如何子類的信息,但也強烈建議您不要!如果您對實際存儲有特殊需求,則只有子類。

A 類集羣意味着實際的類將在運行時被選中。創建的數組爲空,可能不會使用與1000個項目創建的數組相同的類。運行時可以明智地選擇要使用的實現。在實踐中NSMutableArray將橋接CFArray。沒有什麼需要擔心的,但是如果您在調試器中檢查陣列的類型,則可能會看到它,您永遠不會看到NSArray,但通常是NSCFArray

如前所述,子類化與擴展類不一樣。 Objective-C具有類別的概念。一個類別與其他編程語言稱爲混合的類似。

比如你想在NSMutableArray一個方便的方法來對財產的所有成員進行排序,然後在.h文件中定義的類接口如:

@interface NSMutableArray (CWFirstnameSort) 
-(void)sortObjectsByProperty:(NSString*)propertyName; 
@end 

和實現將是:

@implementation NSMutableArray (CWFirstnameSort) 
-(void)sortObjectsByProperty:(NSString*)propertyName; 
{ 
    NSSortDescriptor* sortDesc = [NSSortDescriptor sortDescriptorWithKey:propertName ascending:YES]; 
    [self sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]]; 
} 
@end 

然後使用它只是作爲:

[people sortObjectsByProperty:@"firstName"]; 
5

如果您只是添加自定義方法,請使用NSMutableArray上的類別。它是一個類集羣,所以實現由未記錄的子類提供。你需要提供一些方法來生成你自己的子類。但是,如果您只是添加一個類別,那麼您的自定義方法將在您的應用中在全部NSMutableArrays上運行。

爲了便於比較,下面是我在implementing a custom NSMutableArray subclass回寫的一個例子。

+1

我想評論一下,實現一個自定義類型安全的NSMutableArray子類是浪費時間。編寫這個自定義子類的時間花在編寫一些單元測試來驗證你的* actual *應用程序邏輯是合理的,而不是增加更多混亂。您的額外代碼僅僅是更多的代碼來維護,爲bug和安全缺陷添加更多攻擊向量,而不會爲最終用戶帶來任何實際益處。 – PeyloW 2010-10-06 14:28:49

+1

@PeyloW:一個很好的理論位置,但最終會出現真實世界的代碼,其中類型安全是有用的。正如在我寫的文章中所描述的那樣,您將任何失敗都推向了注入的地步,而不是稍後的任意失誤。通過消除惡意對象替換的可能性,減少了攻擊面。 – 2010-10-06 14:54:03

+1

@PeyloW:還有需要連接到CoreFoundation的真實世界的代碼。這關心你的可變數組實現是否實際上是一個NSMutableArray子類或別的東西;在這一點你的實現有更好的子類NSMutableArray。 – 2010-10-06 15:00:30

4

Objective-C有一種機制可以將方法添加到名爲Categories的現有類中。這樣你就不必創建你自己的子類。

+0

現在鏈接不好。有沒有新的工程? – JohnK 2013-07-20 01:35:25

+0

我更新了鏈接。 – 2013-07-26 15:16:45

2

我個人有T o同意節點忍者和佩羅洛夫,因爲從技術上講他們都有權利。其實,這並沒有多大幫助。

序言: 有在代碼許多陣列,所有到一個僅含有一種,但是不同類型的數據例如classA,classB,classC。

問題: 我可以很容易地混合數組,通過錯誤的傳遞給例如一些選擇器,因爲它們都是NSMutableArray。沒有靜態檢查,只有運行時檢查。

解決方案 - 第一嘗試:NSMutableArray裏的 製作子類,所以編譯器可以進行靜態檢查,並警告有關錯誤的數據類型。

因爲編譯器會發出警告,即使你傳遞錯誤類型-addObject或-objectAtIndex當你重載的人是好的。 這很糟糕,因爲你不能通過這種方式實例化NSMutableArray超類。

解決方案 - 第二次嘗試: 使某些類型的新(代理)類例如NSObject作爲NSMutableArray並添加類型爲NSMutableArray的類成員。

這是一件好事,因爲當你通過錯誤類型-addObject或-objectAtIndex當你重載那些你可以實例NSMutableClass和編譯器檢查。

的,壞的一面是,你需要您使用,不僅那些在課堂上的區別在於數組包含NSMutableArray中的每一個選擇超載。

結論: 當您構建一些在數組中有許多類類型的複雜代碼時,請相信我值得嘗試。簡單地通過這個編譯器向我展示了幾個我將不會識別的錯誤,直到我將在運行時面對它。甚至更糟的是,最終用戶會面對它。

-1

從NSArray的蘋果參考,在方法重寫部分:

NSArray中的任意子類必須覆蓋原始的實例方法計數和objectAtIndex:。這些方法必須在您爲集合元素提供的後備存儲上運行。對於該後備存儲,您可以使用靜態數組,標準NSArray對象或其他數據類型或機制。您也可以選擇部分或完全覆蓋您想要提供替代實現的任何其他NSArray方法。

作爲一個方面說明,在Objective-C中,沒有實際的功能允許您將本類聲明爲抽象類,例如Java中的類。所以,他們所做的就是調用類似下面的代碼,從一些他們想要強制被子類重寫的方法中調用。實際上,他們給出了類的「抽象類」語義。

該方法定義作爲一個抽象方法,如果沒有覆蓋這引發了異常,與下面的輸出:

-someAbstractFooMethod只爲抽象類定義。定義 - [YourClassName someAbstractFooMethod]!

- (void) someAbstractFooMethod 
{ 
    //Force subclassers to override this method 
    NSString *methodName = NSStringFromSelector(_cmd); 
    NSString *className = [self className]; 
    [NSException raise:NSInvalidArgumentException 
       format:@"-%@ only defined for abstract class. Define -[%@ %@]!", methodName, className, methodName]; 
} 
1

這是舊的文章,但認爲我想補充我的經驗。 @PayloW的回答是一個很好的答案,我想回答你的問題很好,但是,沒有人真正回答你的問題倒過來,所以我會在這裏這樣做。

你應該繼承NSMutableArray(或NSArray)嗎?取決於你想要達到的目標。如果你只想添加一個方法來擴展數組的BASIC功能,比如排序,那麼@PayloW的回答Categories就是這樣。然而,如果你想創建一個類似數組的自定義類,那麼是的,子類化NSMutableArray很容易。但是因爲它是一個類集羣,它並不像你期望的那樣完全子類化。通常在子類中,超級類中可用的方法可用於您的子類,或者您可以覆蓋它們。對於Class Clusters,您必須包含Super將要使用的方法,並提供一個超類的實例來包裝這些方法。

下面是你如何繼承NSMutableArray一個例子(或任何類簇):

接口:

@interface MyCustomArrayClass : NSMutableArray { 

    // Backend instance your class will be using 
    NSMutableArray *_backendArray; 
} 

// *** YOUR CUSTOM METHODS HERE (no need to put the Super's methods here) *** 

-(bool)isEmpty; 
-(id)nameAtIndex:(int)index; 
-(int)rowAtIndex:(int)index; 
-(int)columnAtIndex:(int)index; 

@end 

實施:

@implementation MyCustomArrayClass 

-(instancetype)init { 

    if (self = [super init]) {    
     _backendArray = [@[] mutableCopy]; 
    } 

    return self; 
} 

// *** Super's Required Methods (because you're going to use them) *** 

-(void)addObject:(id)anObject { 
    [_backendArray addObject:anObject]; 
} 

-(void)insertObject:(id)anObject atIndex:(NSUInteger)index { 
    [_backendArray insertObject:anObject atIndex:index]; 
} 

-(void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { 
    [_backendArray replaceObjectAtIndex:index withObject:anObject]; 
} 

-(id)objectAtIndex:(NSUInteger)index { 
    return [_backendArray objectAtIndex:index]; 
} 

-(NSUInteger)count { 
    return _backendArray.count; 
} 

-(void)removeObject:(id)anObject { 
    [_backendArray removeObject:anObject]; 
} 

-(void)removeLastObject { 
    [_backendArray removeLastObject]; 
} 

-(void)removeAllObjects { 
    [_backendArray removeAllObjects]; 
} 

-(void)removeObjectAtIndex:(NSUInteger)index { 
    [_backendArray removeObjectAtIndex:index]; 
} 

// *** YOUR CUSTOM METHODS *** 

-(bool)isEmpty { 
    return _backendArray.count == 0; 
} 

-(id)nameAtIndex:(int)index { 
    return ((MyObject *)_backendArray[index]).name; 
} 

-(int)rowAtIndex:(int)index { 
    return ((MyObject *)_backendArray[index]).row; 
} 

-(int)columnAtIndex:(int)index { 
    return ((MyObject *)_backendArray[index]).column; 
} 

@end 

然後像這樣使用:

MyCustomArrayClass *customArray = [[MyCustomArrayClass alloc] init]; 

// Your custom method 
int row = [customArray rowAtIndex:10]; 

// NSMutableArray method 
[customArray removeLastObject]; 

// Your custom class used just like an array !!! 
index = 20; 
MyObject *obj = customArray[index]; 

這一切工作非常好,是乾淨的,實際上非常酷實施和使用。

希望它有幫助。