Domanda

Is there any way to test selectors/methods when all you have is a proxy object?

/* 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];
}
È stato utile?

Soluzione

Apparently you cannot.

The methods suggested by other answers will break easily, here's an example:

  • UINavigationBar instances respond to selector setTranslucent:
  • However setTranslucent: is not tagged with UI_APPEARANCE_SELECTOR in the header file

Therefore the following code

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

will result in a crash.

The only information about which selectors conform to UIAppearance seems to be the UI_APPEARANCE_SELECTOR macro, which is stripped at compile-time.

A runtime check doesn't look to be feasible.


For the sake of completeness, here's an awful (but practical) way of doing it.

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

However, this is very fragile since there's no guarantee that you're catching only the case in which the selector doesn't conform to UIAppearance.

Altri suggerimenti

The best bet seems to be

  1. Make sure that the property in question is defined with UI_APPEARANCE_SELECTOR in the header.
  2. In the code test against the class UINavigationBar itself rather than its appearance

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

or, as another answer suggests,

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

While theoretically proxy is an object of a different class, the appearance proxy seems to be automatically generated by Apple from methods labeled as UI_APPEARANCE_SELECTOR. As the comment in UIAppearance.h states:

To participate in the appearance proxy API, tag your appearance property selectors in your header with UI_APPEARANCE_SELECTOR.

This method will break if you implement it for a property that was available through appearance proxy in the old version of iOS but was removed from the proxy later. Exactly one such example is documented in the same file:

On iOS7 ... the tintColor is now disallowed with the appearance proxy.

So you do need to test for rare cases to make sure the code is not broken by the new iOS, but you're still free of having to code the version numbers manually.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top