2015-10-14 38 views
4

我有一個控制器註冊爲一個觀察員的很多屬性的意見。這是我們的-observeValueForKeyPath::::方法:我如何優化這個巨大的if/else如果塊內observeValueForKey

-(void)observeValueForKeyPath:(NSString *)keyPath 
        ofObject:(id)object 
         change:(NSDictionary *)change 
         context:(void*)context 
{ 

    if(context == kStrokeColorWellChangedContext) 
    { 
     [self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kStrokeColorProperty]; 
    } 
    else if(context == kFillColorWellChangedContext) 
    { 
     [self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kFillColorProperty]; 
    } 
    else if(context == kBodyStyleNumChangedContext) 
    { 
     [self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kBodyStyleNumProperty]; 
    } 
    else if(context == kStyleChangedContext) 
    { 
     [self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kStyleProperty]; 
    } 
    else if(context == kStepStyleChangedContext) 
    { 
     [self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kStepStyleProperty]; 
    } 
    else if(context == kFirstHeadStyleChangedContext) 
    { 
     [self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kFirstHeadStyleProperty]; 
    } 
    else if(context == kSecondHeadStyleChangedContext) 
    { 
     [self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:kSecondHeadStyleProperty]; 
    } 

而實際上還有約3倍以上的這些else if語句。
你可以看到的一件事是,每個塊都有相同的代碼,這使我認爲可以優化它。

我的最初的想法是爲具有NSDictionary稱爲keyPathForContextDictionary,其中鍵是與(的void*型)Context後綴的常數,和的值是適當的字符串常量,由Property後綴表示

然後這個方法只需要一行:

[self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:keyPathForContextDictionary[context]]; 

請注意,我需要使用某種形式的數據結構,以確定使用哪個的keyPath,我不能簡單地用keyPath參數傳遞到方法。這是因爲有多個視圖具有我正在觀察的相同屬性(例如,顏色井具有color屬性)。因此,每個視圖都需要確定一個唯一的鍵路徑,該路徑目前是根據上下文確定的

問題在於,您不能使用void*作爲NSDictionary中的鍵。那麼......有沒有人對我在這裏可以做的事情有任何建議?

編輯: 這裏的常量是如何定義的一個例子:

void * const kStrokeColorWellChangedContext = (void*)&kStrokeColorWellChangedContext; 
void * const kFillColorWellChangedContext = (void*)&kFillColorWellChangedContext; 
void * const kBodyStyleNumChangedContext = (void*)&kBodyStyleNumChangedContext; 
void * const kStyleChangedContext = (void*)&kStyleChangedContext; 

NSString *const kStrokeColorProperty  = @"strokeColor"; 
NSString *const kFillColorProperty  = @"fillColor"; 
NSString *const kShadowProperty   = @"shadow"; 
NSString *const kBodyStyleNumProperty = @"bodyStyleNum"; 
NSString *const kStyleProperty   = @"style"; 
+0

類型參數的'context'是'無效*'這樣你就可以使用任何你想要的類型。你可以將它改爲一個對象 - 你只需要施放它。所有'k $ THINGYColorWellChangedContext'常量的類型是什麼? –

+0

哦對不起,讓我編輯帖子。但'context'後綴表示'void *','property'後綴表示'NSString *' –

+0

請顯示其中一個常量的聲明。 –

回答

1

約什 - 卡斯威爾有一個偉大的答案,但我不想改變我們的常量類型爲NSStrings*

因此,一個解決方案,而不是,是給void*撒入NSValues瓦特/ -valueWithPointer。這樣我可以使用void*作爲鑰匙在我的字典

下面的代碼:

NSString *toolKeyPath = [[ToolController keyPathFromContextDictionary] objectForKey:[NSValue valueWithPointer:context]]; 

    if(toolKeyPath) 
    { 
     if([change objectForKey:NSKeyValueChangeNewKey] == (id)[NSNull null]) 
     { 
     [self setValue:nil forKey:toolKeyPath]; 
     } 
     else 
     { 
     [self setValue:[change objectForKey:NSKeyValueChangeNewKey] forKey:toolKeyPath]; 
     } 
    } 

和字典:

+(NSDictionary*) keyPathFromContextDictionary 
{ 
    return @{ 
      [NSValue valueWithPointer:kStrokeColorWellChangedContext] : kStrokeColorProperty, 
      [NSValue valueWithPointer:kFillColorWellChangedContext] : kFillColorProperty, 
      [NSValue valueWithPointer:kBodyStyleNumChangedContext] : kBodyStyleNumProperty, 
      [NSValue valueWithPointer:kStyleChangedContext] : kStyleProperty, 
      [NSValue valueWithPointer:kStepStyleChangedContext] : kStepStyleProperty, 
      [NSValue valueWithPointer:kFirstHeadStyleChangedContext] : kFirstHeadStyleProperty, 
      [NSValue valueWithPointer:kSecondHeadStyleChangedContext] : kSecondHeadStyleProperty, 
      [NSValue valueWithPointer:kShadowChangedContext] : kShadowProperty, 
      [NSValue valueWithPointer:kStrokeWidthChangedContext] : kStrokeWidthProperty, 
      [NSValue valueWithPointer:kBlurRadiusChangedContext] : kBlurRadiusProperty, 
      [NSValue valueWithPointer:kFontSizeChangedContext] : kFontSizeProperty 
     }; 
} 
1

類型void *與其說是一類本身就是你要匹配,因爲它是「通用指針」。它精確地用於context參數,以便您可以使用任何您喜歡的基礎類型,包括對象類型。你所要做的就是進行適當的演員。

因此,您可以將您的kTHINGYChangedContext s或NSString s或您喜歡的任何其他物體輕鬆地更改,然後在上下文 - >鍵路徑映射中將它們用作鍵。

開始:

NSString * const kStrokeColorWellChangedContext = @"StrokeColorWellChangedContext"; 

當您註冊的觀察,你必須執行橋接轉換:

[colorWell addObserver:self 
      forKeyPath:keyPath 
       options:options 
       context:(__bridge void *)kStrokeColorWellChangedContext]; 

然後,當觀察時,你做相反的轉換:

-(void)observeValueForKeyPath:(NSString *)keyPath 
        ofObject:(id)object 
         change:(NSDictionary *)change 
         context:(void*)ctx 
{ 
    NSString * context = (__bridge NSString *)ctx; 
    // Use context, not ctx, from here on. 
} 

然後繼續從那裏查找關鍵路徑。

+0

謝謝你的答案!我的大腦有點油炸,很難弄清楚__bridge的作用。你能否偶然給你解釋它的目的?如果不是那麼酷,我可以坐下來閱讀文檔。無論哪種方式,我現在要嘗試您的解決方案 –

+0

ARC不會讓您只是將對象投射到'void *',因爲它無法跟蹤您在此之後對它做什麼。你可以用'__bridge'來明確「是的,ARC,我知道我在做什麼,不用擔心這個指針」。更多信息:[ARC和橋接轉換](http://stackoverflow.com/q/7036350) –

+0

Gotcha。你知道它具體做什麼,這是告訴ARC不要擔心?我認爲我已經看到了在將C++中的對象轉換爲Objective-C對象時所使用的方法;所以我原本以爲它只用於C++的東西 –