3

当你只有一个代理对象时,有什么方法可以测试选择器/方法?

/* 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];
}
4

2 回答 2

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

于 2013-09-27T19:38:24.457 回答
1

最好的选择似乎是

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

     if ([(some UINavigationBar object) respondsToSelector:@selector(setBarTintColor:)])...
    

或者,正如另一个答案所暗示的,

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

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

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

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

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

因此,您确实需要针对罕见情况进行测试,以确保新 iOS 不会破坏代码,但您仍然不必手动编写版本号。

于 2013-09-27T19:20:56.500 回答