2011-04-08 71 views
12

在Objective-C中是否有任何技術來模擬特徵或混入?Objective-C支持traits/mixins嗎?

在Scala中,例如,我可以做這樣的事情:

trait ControllerWithData { 
    def loadData = ... 
    def reloadData = ... 
    def elementAtIndex = ... 
} 

trait ControllerWithStandardToolbar { 
    def buildToolbar = ... 
    def showToolbar = ... 
    def hideToolbar = ... 
} 

class MyTableController extends ControllerWithData 
         with ControllerWithStandardToolbar { 
    def loadView = { 
    super.loadView 

    loadData 
    buildBar 
    } 
} 

它基本上合併(或混合的)的功能多塊成一個類的方法。所以現在我擁有一個全功能的UIViewController,它是我的所有控制器的子類,但是如果我可以將它分解並讓特定的控制器繼承特定的行爲,它會更加整潔。

+0

護理解釋什麼特質與混入了?這些概念可能得到支持,但以不同的名稱知道。 – 2011-04-08 21:50:35

+0

當然,讓我修改。 – Bill 2011-04-08 21:55:59

+0

貌似有人在這裏實現了Obj-C特質:http://etoileos.com//news/archive/2011/07/12/1427/ – Bill 2011-07-12 19:08:59

回答

18

沒有直接的語言支持,但您可以完成類似的消息轉發。假設你有特質類「Foo」和「Bar」,分別定義方法「-doFoo」和「-doBar」。你可以定義你的班級有特點,像這樣:

@interface MyClassWithTraits : NSObject { 
    NSMutableArray *traits; 
} 
@property (retain) NSMutableArray* traits; 

-(void) addTrait:(NSObject*)traitObject; 
@end 

@implementation MyClassWithTraits 
@synthesize traits; 

-(id)init { 
    if (self = [super init]) { 
     self.traits = [NSMutableArray array]; 
    } 
    return self; 
} 

-(void) addTrait:(NSObject*)traitObject { 
    [self.traits addObject:traitObject]; 
} 

/* Here's the meat - we can use message forwarding to re-send any messages 
    that are unknown to MyClassWithTraits, if one of its trait objects does 
    respond to it. 
*/ 
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector { 
    // If this is a selector we handle ourself, let super handle this 
    if ([self respondsToSelector:aSelector]) 
     return [super methodSignatureForSelector:aSelector]; 

    // Look for a trait that handles it 
    else 
     for (NSObject *trait in self.traits) 
      if ([trait respondsToSelector:aSelector]) 
       return [trait methodSignatureForSelector:aSelector]; 

    // Nothing was found 
    return nil; 
} 

-(void) forwardInvocation:(NSInvocation*)anInvocation { 
    for (NSObject *trait in self.traits) { 
     if ([trait respondsToSelector:[anInvocation selector]]) { 
      [anInvocation invokeWithTarget:trait]; 
      return; 
     } 
    } 

    // Nothing was found, so throw an exception 
    [self doesNotRecognizeSelector:[anInvocation selector]]; 
} 
@end 

現在,您可以創建MyClassWithTraits的實例,並添加你想要什麼「特質」的對象:

MyClassWithTraits *widget = [[MyClassWithTraits alloc] init]; 
[widget addTrait:[[[Foo alloc] init] autorelease]]; 
[widget addTrait:[[[Bar alloc] init] autorelease]]; 

你可以做在MyClassWithTraits'-init方法中調用-addTrait:,如果您希望該類的每個實例具有相同種類的特徵。或者,你可以像我在這裏做的那樣做,它可以讓你爲每個實例分配一組不同的特徵。

然後你就可以調用-doFoo-doBar,好像他們是通過插件實現的,即使消息被轉發到其特點的一個對象:

[widget doFoo]; 
[widget doBar]; 

編輯:添加錯誤處理。 )

+0

太棒了!當你看到這個問題時,你不知道是什麼特質,你搜索,甚至實施這個! 我想知道一件事,爲什麼methodSignatureForSelector需要被覆蓋。是不是forwardInvocation足夠? 何時調用methodSignatureForSelector?流量是什麼? – 2013-08-21 18:56:14

-4
+1

不幸的是,我不這麼認爲。我認爲這一點,我認爲如果有辦法做到這一點,它將涉及類別。但是類別可以讓你將方法混合到一個特定的類中,而不是多個類(對嗎?),所以我認爲它們不夠用。但我可能是錯的。 – Bill 2011-04-08 22:11:13

+0

類別確實在單個類中定義。但是,從該類繼承的其他類也繼承了這些類別。 – Zr40 2011-04-08 22:17:53

+4

類別絕對不是斯卡拉意義上的特徵。他們更像是C#領域的部分類。 – 2011-04-08 23:08:24

0

Objective-C不支持Traits或Mixins,您只有內置的類別選項。 但幸運的是Objective-C Runtime幾乎包含了所有用於實現自己的想法的工具,如果混合或者在運行時爲你的類添加方法和屬性。你可以閱讀更多關於它的Objective-C運行時爲您提供了對蘋果的文檔網站的機會Objective-C Runtime Docs

的理念是:

1)您可以創建一個Objective-C協議(密新),在其中將聲明屬性和方法。

2)然後創建一個類(Mixin實現),它將實現此協議中的方法。

3)你讓你的某個類想要提供mixin合成的可能性,以符合該協議(Mixin)。當您的應用程序啓動時,您將Objective-C運行時的所有實現(Mixin實現)類和(Mixin中聲明的)屬性添加到您的類中。

5)瞧:)

或者你可以使用一些現成的開源項目,如「Alchemiq