我如何优化这个巨大的 if/else if 块在 observeValueForKey 中
How can I optimize this huge if/else if block within observeValueForKey
我有一个注册为观察者的控制器,用于查看很多属性。这是我们的 -observeValueForKeyPath::::
方法:
-(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];
}
实际上这些 else if
语句的数量是原来的 3 倍。
你可以看到的一件事是每个块都有相同的代码,这让我觉得可以优化它。
我最初的想法是有一个名为 keyPathForContextDictionary
的 NSDictionary
,其中键是带有 Context
后缀(void*
类型)的常量,而值是适当的字符串常量,由 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";
类型 void *
与其说是您必须匹配的类型本身,不如说是 "generic pointer"。它精确地用于 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.
}
然后从那里继续查找关键路径。
Josh Caswell 的回答很好,但我不想将常量的类型修改为 NSStrings*
因此,一个解决方案是将 void*
转换为 NSValues
w/ -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
};
}
我有一个注册为观察者的控制器,用于查看很多属性。这是我们的 -observeValueForKeyPath::::
方法:
-(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];
}
实际上这些 else if
语句的数量是原来的 3 倍。
你可以看到的一件事是每个块都有相同的代码,这让我觉得可以优化它。
我最初的想法是有一个名为 keyPathForContextDictionary
的 NSDictionary
,其中键是带有 Context
后缀(void*
类型)的常量,而值是适当的字符串常量,由 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";
类型 void *
与其说是您必须匹配的类型本身,不如说是 "generic pointer"。它精确地用于 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.
}
然后从那里继续查找关键路径。
Josh Caswell 的回答很好,但我不想将常量的类型修改为 NSStrings*
因此,一个解决方案是将 void*
转换为 NSValues
w/ -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
};
}