如何在Objective-C中测试代理对象上的选择器?

时间:2013-09-27 18:31:19

标签: ios objective-c uiappearance respondstoselector

当你拥有的只是代理对象时,有没有办法测试选择器/方法?

/* Proxy which may forward
 * the method to some other object */ 
id proxy = [UINavigationBar appearance];

/* This condition returns FALSE
 * despite the fact that the method would have otherwise been
 * successfully executed at its destination */
if([proxy respondsToSelector:@selector(setBarTintColor:)]){
    [proxy setBarTintColor:color];
}

2 个答案:

答案 0 :(得分:2)

显然你不能。

其他答案建议的方法很容易破解,这是一个例子:

  • UINavigationBar个实例会回应选择器setTranslucent:
  • 但是,setTranslucent:未在标头文件
  • 中标记为UI_APPEARANCE_SELECTOR

因此以下代码

if([[UINavigationBar class] instancesRespondToSelector:@selector(setTranslucent:)]) {
    [[UINavigationBar appearance] setTranslucent:NO]; // BUM!
}

会导致崩溃。

有关哪些选择器符合UIAppearance的唯一信息似乎是UI_APPEARANCE_SELECTOR宏,它在编译时被删除。

运行时检查看起来不可行。


为了完整起见,这是一种糟糕(但实用)的方式。

@try {
    [[UINavigationBar appearance] setTranslucent:YES];
} @catch (NSException *exception) {
    if ([exception.name isEqualToString:@"NSInvalidArgumentException"])
        NSLog(@"Woops");
    else
        @throw;
}

然而,这非常脆弱,因为无法保证您只捕获选择器不符合UIAppearance的情况。

答案 1 :(得分:1)

最好的选择似乎是

  1. 确保标题中的UI_APPEARANCE_SELECTOR定义了相关媒体资源。
  2. 在针对类UINavigationBar本身的代码测试中,而不是其外观

     if ([(some UINavigationBar object) respondsToSelector:@selector(setBarTintColor:)])...
    
  3. 或者,如另一个答案所示,

    if ([UINavigationBar.class instancesRespondToSelector:@selector(setBarTintColor:)])...
    

    虽然理论上proxy是不同类的对象,但外观代理似乎是Apple自动从标记为UI_APPEARANCE_SELECTOR的方法生成的。正如UIAppearance.h中的评论所述:

      

    要参与外观代理API,请使用UI_APPEARANCE_SELECTOR在标题中标记外观属性选择器。

    如果您为旧版iOS中通过外观代理提供但后来从代理中删除的属性实现此方法,则此方法将中断。在同一个文件中记录了一个这样的例子:

      

    在iOS7上...... tintColor现在不允许使用外观代理。

    因此,您需要测试极少数情况,以确保代码不会受到新iOS的破坏,但您仍然无需手动编写版本号。