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
相关产品推荐
相关产品推荐

