2011-12-28 60 views
9

Joe Conway在他的iOS編程書的第二章中描述了在子類中使用類方法中的'self'。我理解這個概念並對子類問題有一個疑問。如何正確覆蓋子類中Objective-C中的類方法?

背景:我們創建了一個藏類,其類方法+ randomPossession看起來是這樣的:

+(id)randomPossession 
{ 
NSArray *randomAdjectiveList = [NSArray arrayWithObjects:@"Fluffy", @"Rusty", @"Shiny", nil]; 
NSArray *randomNounList = [NSArray arrayWithObjects:@"Bear", @"Spork", @"Mac", nil]; 

unsigned long adjectiveIndex = rand() % [randomAdjectiveList count]; 
unsigned long nounIndex = rand() % [randomNounList count]; 

NSString *randomName = [NSString stringWithFormat:@"%@ %@", [randomAdjectiveList objectAtIndex:adjectiveIndex], [randomNounList objectAtIndex:nounIndex]]; 

int randomValue = rand() % 100; 

NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c", 
           '0' + rand() % 10, 
           'A' + rand() % 10, 
           '0' + rand() % 10, 
           'A' + rand() % 10, 
           '0' + rand() % 10]; 

Possession *newPossession = [[self alloc] initWithPossessionName:randomName valueInDollars:randomValue serialNumber:randomSerialNumber]; 

return [newPossession autorelease]; 
} 

我知道,返回值應該真正ID類型,使得ID newPossession = ...

我子類佔有並創建了一個類中調用BallGlove,其中包括一個新的伊娃,名優產品,一個NSString *

我推翻了+ randomPossession在BallGlove如下:

+(id)randomPossession 
{ 
BallGlove *myGlove = [super randomPossession]; 

NSArray *brandNames = [NSArray arrayWithObjects:@"Rawlings", @"Mizuno", @"Wilson", nil]; 

unsigned long randomNameIndex = rand() % [brandNames count]; 

[myGlove setBrandName:[brandNames objectAtIndex:randomNameIndex]]; 

NSLog(@"myGlove is of type class: %@", [self class]); 

return myGlove; 
} 

我的問題是這樣的:我超越了這種社會適用和可接受的這種方法的方式(即,通過捕獲變量中的超級實現來平行-init格式,相應地操作變量然後返回它?我的輸出顯示返回的對象是BallGlove的一個實例,但是我對可接受的實現感興趣。提前致謝。

回答

0

從技術上講沒問題。

雖然我會提出一個替代方案。如果你的基類已經有了一個指定的公共初始化器(你可能想創建並從該工廠類方法中調用),然後在你的子類的類中使用這個初始化器(甚至是你的子類中的一個新初始化器)方法。

這不是更多的代碼,但在我看來更容易遵循和麪向未來。初始化器可能在某個時候也很方便,但它當然不是每個應用程序的解決方案。

+0

+ randomPossession在基類中實際上調用了指定的初始值設定項。類方法也爲該初始化器創建「隨機」值。如果我按照我相信你所建議的做法,我必須在重寫的類方法(+ randomPossession)中複製這些「隨機」值的代碼,並重寫指定的初始化程序以調用BallGlove類的新初始化程序,添加我的附加iVar信息。爲什麼重寫階級方法,因爲我在一般意義上建議有害的?難道它不能像鏈接初始化器嗎? –

+0

我不認爲它太醜陋 - 它不是一個很常見的模式。這個問題本身就是一個很好的指標。 :)有一點與子類化有點模糊的是方法的名稱,好的命名是關於objective-c的最好的事情之一。所以在你的情況下,一個方法+ randomBallGlove可能比重寫更好。 – Eiko

+0

你建議我改爲創建一個名爲+ randomBallGlove的類方法,除了需要的自定義實現之外,還包括+ randomPossession的實現?聽起來不錯。對於Possession的子類,仍然可以使用+ randomPossession,你有什麼建議?重寫方法並讓它拋出一個異常,指示使用+ randomBallGlove?這通常是這種事件的正常協議嗎?我知道這個例子在現實中可能並不存在,我只是在學習,並且很好奇接受的實現。謝謝你的幫助! –

1

是的,可以接受這樣的初始化。事實上,這在大多數情況下是如何完成的。我的意思是,這就是首先從超級類繼承的原因。你想要的東西除了超級課程中的內容。因此,您將代碼插入特定於繼承類的代碼中,並且應該這樣做。

我認爲你想如何初始化BallGlove對象也是你如何定義你的繼承方法的一個因素。調用Possession init或調用BallGlove init(問題不在於創建類的實例是唯一使用類方法的地方)。所以它歸結爲創建對象的邏輯,也就是說你如何描述對象 - 你是否確保你的類方法以符合對象標準的方式描述它,而不是成爲通用的Possession對象。我的回答是,如果你能夠正確實施,那麼使用平行的類方法是可以接受的。

另外,如果你在你的超類返回Possession類型的也沒關係,因爲id可以指向任何類類型

+0

感謝您的回覆。但這並沒有真正回答這個問題。這個問題涉及重寫類方法的格式。除了爲子類生成便利方法的必要性之外,我不確定是否覆蓋類方法的原因。就這樣,我還沒有看到任何東西。是否有任何例子證明我使用的格式是合適的? –

+0

@BrianPalma對不起。也許我沒有正確回答。我真的不明白爲什麼以類似於初始化器的方式爲類方法做它是不可接受的。我認爲它的工作方式是,類方法傾向於調用init方法,執行一些代碼並返回一個自動發佈的版本,這正是你正在做的。我不知道是否有例子來驗證你的格式,但我也看不到任何可能出現的問題。讓我檢查一些示例代碼並回復給您。 – MadhavanRP

2

沒錯的對象,這是一個非常明智的辦法做到這一點。類方法和普通方法之間沒有什麼特別的區別 - 只是一個是由一個類執行的,另一個是由一個實例執行的。

2

當你重寫一個類方法的時候,你可以決定在超級實現的幫助下實現它,或者沒有它。這完全取決於你。 Overiding init是一個完全不同的故事,不僅因爲它是一種實例方法,而且因爲它有一個與之相關的約定/契約。請記住,例如Liskov Subtitution原則的方法不應該被違反。

您對類方法的重寫是完全正確的,儘管我會考慮過分類方法的設計氣味。儘管在Objective-C中它有很好的可能性,但它不在其他語言中,並且出於非常好的原因。多態性作爲一個概念更好地綁定到可以用作彼此替代的實例,而使用類方法打破了概念(即沒有真正的結構)。它很聰明,但不是非常直觀和靈活。

+0

感謝您的回覆。正如我在下面提到的,我不知道在子類中重寫某個特定類方法的用法,除了方便方法中的較少代碼之外。在這一點上,我無意這樣做。如果出現這種情況,我只是好奇而已。 –

+0

@BrianPalma:除了「便利方法」之外,有理由重寫類方法。例如,NSView的'defaultMenu'和'requiresConstraintBasedLayout'類方法存在只是被覆蓋。 – Chuck

+0

@Chuck對不起,我想我錯過了,或者太新了!就模型對象而言,我的意思是 「便利方法」。我明白iOS和OS X中存在純粹被覆蓋並需要[super someClassMethodHere]的類方法; –