You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 15:06:06