How to test a protocol for a method?

2020-05-23 08:01发布

Before iOS 4, I used to add a observer to each MKAnnotationView added to the map view, listening for it's selected method, so I know when the user has tapped on a pin.

This worked fine up to iOS 4.2. I've noticed on the release noted annotation views are actually being reused and it somehow messes up with the observers.

So I figure I can use the -mapview:didSelectAnnotationView: method from MKMapViewDelegate for my needs, but that has only been added to iOS 4.0 SDK.

So, to maintain compatibility, I'd like to implement this method on my delegate and conditionally check for the presence of this method on the MKMapViewDelegate protocol so that if it's not present, I will add my observer to the annotation view.

How can I do this for a protocol method, similarly for how we check if a class is not nil?

UPDATE:

As Daniel Dickison pointed out, I can't use respondsToSelector:, because my delegate has -mapview:didSelectAnnotationView: implemented for 4.0+ devices. What I need is to check if the protocol on that device has the optional -mapview:didSelectAnnotationView: method OR if MKMapView will look for that method on it's delegate.

I ended up doing a test for the current iOS version running. If it's higher than 4.0, MKMapView will look for that method and call it.

if ([[[UIDevice currentDevice] systemVersion] doubleValue] < 4.0)
    [self setupObserver];

This solves the original problem, but it would still be interesting to check the actual protocol for the method somehow.

5条回答
我想做一个坏孩纸
2楼-- · 2020-05-23 08:37

I think you want NSObject conformsToProtocol - something like:

BOOL test = [myObject conformsToProtocol:@protocol(MKMapViewDelegate)];
查看更多
手持菜刀,她持情操
3楼-- · 2020-05-23 08:42

That's a tricky one. So if I'm understanding your question correctly, you want to find out, at runtime, whether the map view sends the mapView:didSelectAnnotationView: message to its delegate. But you can't use conformsToProtocol: or respondsToSelector: because you're implementing the delegate so obviously you're adopting the protocol and implementing the method.

The only thing I can think of is to check for some other method that was added to MKMapView (not the delegate) in iOS 4, like: mapRectThatFits:.

Another possibility is to use the Objective-C runtime library to query the Protocol object. But this is probably overkill, and also I don't think it will work because the Protocol object is created by the compiler when you build your app, and it's possible you'll get the UIKit SDK-defined MKMapViewDelegate protocol object instead of whatever the runtime was compiled with.

查看更多
beautiful°
4楼-- · 2020-05-23 08:44

I've taken a slightly different approach.

I simply use an #ifdef (__iPHONE_OS_VERSION_MIN_REQUIRED... and add observer if necessary, along with using the -mapview:didSelectAnnotationView: delegate method.

查看更多
【Aperson】
5楼-- · 2020-05-23 08:58

Because there is no object instance you can ask if it responds to a message selector, and you already know the protocol is supported but you are just looking for one method within - you need to use protocol_getMethodDescription, like so (method is class instance and optional) where you check for a nil return value:

#import <objc/runtime.h>

struct objc_method_description hasMethod = protocol_getMethodDescription(@protocol(MKMapViewDelegate), @selector(mapView:didSelectAnnotationView:), NO, YES);

if ( hasMethod.name != NULL )
{
...
}
查看更多
ら.Afraid
6楼-- · 2020-05-23 09:02

I would use the respondsToSelector: method because that allows you to check for specific methods (which it sounds like you're doing, otherwise, if you're looking to check for a specific protocol, @Eric's answer is a good one). This SO post talks about using it this way.

Basically, the way you'd use it is

SEL methodName = @selector(mymethod:);
BOOL test = [object respondsToSelector:methodName];
查看更多
登录 后发表回答