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

Swift实现Method Swizzling时遇set(_:forKey:)歧义错误求助

解决UserDefaults Method Swizzling中的Selector歧义问题

在对UserDefaults进行Method Swizzling时,编写原始方法的#selector遇到歧义错误,代码示例如下:

extension UserDefaults {
    @objc func customSetForKey(value: Any?, forKey: String) {
        UserDefaults.standard.set(value, forKey: "test" + forKey)
    }
    
    static func swizzleMethod() {
        let orginalSelector = #selector(set(_:forKey:))
        let origanlSetMethod = class_getInstanceMethod(self, orginalSelector)
        let swizzleMethod = class_getInstanceMethod(self, #selector(customSetForKey(value:forKey:)))
    }
}

报错信息:

'Ambiguous use of 'set(_:forKey:)''

问题核心是UserDefaults的set(_ value: Any?, forKey: String)首参数无外部名,而Swift编译器无法区分多个重载的set方法,导致Selector歧义。


方案1:通过类型标注明确方法签名

直接在#selector中指定方法的完整类型签名,让编译器明确你要引用的重载版本:

extension UserDefaults {
    @objc func customSetForKey(value: Any?, forKey key: String) {
        UserDefaults.standard.set(value, forKey: "test" + key)
    }
    
    static func swizzleMethod() {
        // 明确指定方法的参数和返回值类型,消除歧义
        let originalSelector = #selector(set(_:forKey:) as (Any?, String) -> Void)
        guard let originalSetMethod = class_getInstanceMethod(self, originalSelector) else { return }
        
        let swizzleSelector = #selector(customSetForKey(value:forKey:))
        guard let swizzleMethod = class_getInstanceMethod(self, swizzleSelector) else { return }
        
        // 执行Swizzling逻辑
        if class_addMethod(self, originalSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod)) {
            class_replaceMethod(self, swizzleSelector, method_getImplementation(originalSetMethod), method_getTypeEncoding(originalSetMethod))
        } else {
            method_exchangeImplementations(originalSetMethod, swizzleMethod)
        }
    }
}

方案2:声明签名一致的私有辅助方法

在扩展中声明一个和目标方法签名完全一致的私有@objc方法,通过它来获取无歧义的Selector:

extension UserDefaults {
    @objc func customSetForKey(value: Any?, forKey key: String) {
        UserDefaults.standard.set(value, forKey: "test" + key)
    }
    
    // 声明与目标set方法签名一致的私有辅助方法
    @objc private func dummySet(_ value: Any?, forKey key: String) {}
    
    static func swizzleMethod() {
        // 通过辅助方法获取无歧义的Selector
        let originalSelector = #selector(dummySet(_:forKey:))
        guard let originalSetMethod = class_getInstanceMethod(self, originalSelector) else { return }
        
        let swizzleSelector = #selector(customSetForKey(value:forKey:))
        guard let swizzleMethod = class_getInstanceMethod(self, swizzleSelector) else { return }
        
        // 执行Swizzling逻辑
        if class_addMethod(self, originalSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod)) {
            class_replaceMethod(self, swizzleSelector, method_getImplementation(originalSetMethod), method_getTypeEncoding(originalSetMethod))
        } else {
            method_exchangeImplementations(originalSetMethod, swizzleMethod)
        }
    }
}

补充说明

出现歧义的根本原因是UserDefaults存在多个重载的set方法(比如set(_:forKeyPath:)、set(_:forKey:)等),这些方法在Objective-C中签名不同,但Swift编译器仅通过set(_:forKey:)的Selector语法无法区分。通过上述两种方式明确方法的具体签名,就能消除编译器的歧义判断。

内容的提问来源于stack exchange,提问作者VIJAY SINGH RAGHAV

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 11:40:38