如何为已遵循UIGestureRecognizerDelegate的Swift类A添加可选协议方法?
解决方案:为第三方类注入UIGestureRecognizerDelegate可选方法
为什么你的扩展方案无效?
UIGestureRecognizerDelegate是Objective-C协议,系统调用其可选方法时依赖Objective-C Runtime查找方法实现。Swift普通扩展中的方法默认不会被注册到Objective-C Runtime,因此系统无法找到你写的方法,自然不会触发断点。而子类化的方法会自动被Objective-C Runtime识别(因为UIView是Objective-C类,其子类的方法会加入Runtime方法表),所以能正常生效。
可行方案1:带@objc标记的类扩展
如果第三方类A没有实现gestureRecognizer:shouldRequireFailureOfGestureRecognizer:方法,直接给A的扩展添加@objc标记即可让方法被Objective-C Runtime识别:
extension A { @objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool { print("abcd") // 根据需求返回对应布尔值,示例返回false return false } }
注意:如果类A本身已经实现了这个方法,扩展中的方法不会覆盖原实现(Swift扩展方法优先级低于类自身方法),此时需要用方法交换方案。
可行方案2:方法交换(Method Swizzling)
如果类A已有该方法的实现,或者你需要确保所有A的实例(包括子类)都执行你的逻辑,可通过方法交换注入代码:
import ObjectiveC extension A { private static let swizzleSetup: Void = { let originalSelector = #selector(UIGestureRecognizerDelegate.gestureRecognizer(_:shouldRequireFailureOf:)) let swizzledSelector = #selector(swizzled_shouldRequireFailureOf) guard let originalMethod = class_getInstanceMethod(A.self, originalSelector), let swizzledMethod = class_getInstanceMethod(A.self, swizzledSelector) else { return } // 若原类未实现该方法,先添加我们的方法到原方法的selector let didAddMethod = class_addMethod(A.self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) if didAddMethod { // 替换swizzledSelector的实现为原方法(如果原方法存在的话) class_replaceMethod(A.self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)) } else { // 原方法已存在,直接交换实现 method_exchangeImplementations(originalMethod, swizzledMethod) } }() @objc private func swizzled_shouldRequireFailureOf(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool { // 先执行你的自定义逻辑 print("abcd") // 调用原方法的实现(交换后,调用swizzled方法实际是调用原方法) return swizzled_shouldRequireFailureOf(gestureRecognizer, shouldRequireFailureOf: otherGestureRecognizer) } } // 在App启动时触发方法交换,比如在AppDelegate的didFinishLaunchingWithOptions中: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { _ = A.swizzleSetup return true }
这种方案会修改类A的方法实现,所有继承自A的子类都会自动生效,无需重复代码。
注意事项
- 方法交换属于Runtime黑魔法,使用前需确认第三方类A的实现细节,避免破坏原有逻辑。
- 若使用@objc扩展方案,确保方法签名与Objective-C协议完全一致(参数、返回值类型不能错)。
内容的提问来源于stack exchange,提问作者Valentin Shamardin
相关产品推荐
相关产品推荐

