4

У меня есть контроллер, который зарегистрирован как наблюдатель для LOT свойств на представлениях. Это наш -observeValueForKeyPath:::: метод:Как я могу оптимизировать этот огромный if/else if block в пределах наблюденияValueForKey

-(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]; 
    } 

И есть на самом деле о 3x больше этих else if заявлений.
Одна вещь, которую вы можете видеть, состоит в том, что каждый блок имеет тот же код, что заставляет меня думать, что можно оптимизировать это.

Моя первая мысль была иметь NSDictionary называется keyPathForContextDictionary, где ключи константы с Context суффиксом (типа void*), а значения соответствующих строковых констант, обозначаемые суффиксом Property

Тогда это метод потребуется только одну строки:

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

Обратите внимание, что мне нужно использовать структуру данных некоторого вида, чтобы определить, какие использовать ключевой путь, и я не могу просто использовать keyPath аргумент, передаваемый в метод , Это связано с тем, что есть несколько видов, которые имеют то же свойство, что и у меня (например, цветные лунки имеют свойство color). Поэтому каждому виду необходимо определить уникальный ключевой путь, который в настоящее время определяется исходя из контекста

Проблема заключается в том, что вы не можете использовать void* как ключи в NSDictionary. Итак ... у кого-нибудь есть рекомендации по тому, что я могу здесь сделать?

EDIT: Вот пример того, как константы определены:

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

Джош Caswell имел большой ответ, но я не хочу, чтобы изменить тип наших постоянных в 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++. –