Objective-C如何从函数调用串获取selector实现可选协议安全调用
可选协议方法安全调用最优方案
不推荐的宏实现方案
你期望的仅传入对象和完整方法调用的双参数宏,受限于OC预编译规则和@selector语法限制,无法通过原生宏能力实现。如果强行通过C字符串解析提取selector,会存在编译时无法校验方法正确性、参数传递异常等风险,生产环境不建议使用。
如果你可以接受稍微调整调用形式,下面的简化宏可以满足需求,不需要重复写判断逻辑:
#define SAFECALL(obj, sel_name, ...) \ do { \ SEL sel = @selector(sel_name); \ if ([obj respondsToSelector:sel]) { \ [obj performSelector:sel withObjects:__VA_ARGS__]; \ } \ } while(0)
使用方式:
// 无参数 SAFECALL(subModule, someOptionalMethod); // 带1个参数 SAFECALL(subModule, scrollViewWillBeginDragging:, nil); // 带2个参数 SAFECALL(subModule, handleParamA:paramB:, valueA, valueB);
注:
performSelector:withObjects:是NSObject的常用扩展方法,最多支持传3个参数,如果需要更多参数可以自行扩展该方法的实现。
更优雅的无宏实现方案
最推荐使用消息转发封装的方案,完全贴合OC语法,无任何语法陷阱,代码可读性更高:
实现通用安全调用器
#import <objc/runtime.h> @interface ProtocolSafeInvoker : NSObject /// 生成协议方法安全调用器,调用协议@optional方法无需手动判断响应 /// @param target 实际实现协议的对象 /// @param protocol 目标协议 + (instancetype)invokerWithTarget:(id)target protocol:(Protocol *)protocol; @end @implementation ProtocolSafeInvoker { __weak id _target; Protocol *_protocol; } + (instancetype)invokerWithTarget:(id)target protocol:(Protocol *)protocol { ProtocolSafeInvoker *invoker = [ProtocolSafeInvoker new]; invoker->_target = target; invoker->_protocol = protocol; return invoker; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { if ([_target respondsToSelector:sel]) { return [_target methodSignatureForSelector:sel]; } // 不响应的方法返回空签名,避免崩溃 return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } - (void)forwardInvocation:(NSInvocation *)invocation { if ([_target respondsToSelector:invocation.selector]) { [invocation invokeWithTarget:_target]; } } @end
使用方式
// 初始化时生成一次安全调用器,强转为对应协议类型即可 id<SubModuleProtocol> safeSubModule = (id<SubModuleProtocol>)[ProtocolSafeInvoker invokerWithTarget:subModule protocol:@protocol(SubModuleProtocol)]; // 后续任意调用协议的@optional方法,无需做响应判断 [safeSubModule scrollViewWillBeginDragging:nil]; [safeSubModule scrollViewDidScroll:scrollView];
该方案支持任意参数个数、任意返回值的协议方法调用,不需要修改原有协议和实现代码,适配性极强。
内容的提问来源于stack exchange,提问作者FaceWaller
相关产品推荐
相关产品推荐

