iOS 方法交换后为什么调自身?
在 iOS 开发中,方法交换(Method Swizzling)是一种强大的技术,它允许开发者在运行时替换类中的方法实现,以便在调用某个方法时添加自定义逻辑。然而,方法交换后,开发者可能会发现调用原方法时,仍然会触发自身的实现。本文将探讨这一现象的原理,并提供代码示例以及对应的序列图和流程图。
方法交换的原理
在 Objective-C 中,方法的实现是通过选择器(Selector)来找到的。当我们使用 Method Swizzling 时,实际上是交换了选择器与其对应的方法实现。这样,一旦调用了某个方法,实际上调用的是我们交换后的方法实现。
示例代码
下面是一个简单的示例,演示如何通过方法交换来替换 viewDidLoad
方法:
#import <objc/runtime.h>
@implementation MyViewController
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalSelector = @selector(viewDidLoad);
SEL swizzledSelector = @selector(my_viewDidLoad);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
- (void)my_viewDidLoad {
// 在原本的 viewDidLoad 中添加自定义逻辑
[self my_viewDidLoad]; // 这里调用的是原始的 viewDidLoad 方法
NSLog(@"View Did Load Swizzled");
}
@end
在这个示例中,当 viewDidLoad
被调用时,实际会调用 my_viewDidLoad
实现,而在该实现中又调用了 my_viewDidLoad
造成递归,最终导致程序崩溃。原因在于,方法交换后,my_viewDidLoad
实际上调用的是 viewDidLoad
。
方法交换后的自调用现象
当你进行方法交换时,开发者需要谨慎处理自调用的问题。由于方法本质上仍然是同一个选择器,导致在调用新实现时,可能会再次调用新实现造成无限递归。因此,如果要确保能在新实现中调用原实现,必须使用一个透明的方法名,以避免调用到新方法。
流程图
下面是一个描述方法交换过程的流程图:
flowchart TD
A[开始] --> B[获取原始方法与交换方法]
B --> C[执行方法交换]
C --> D{调用原始方法}
D -->|是| E[调用新实现]
E --> F{是否再调用}
F -->|是| G[递归调用]
G --> H[崩溃]
F -->|否| I[结束]
D -->|否| I
序列图
下面是对应的序列图,描述了方法交换后调用过程:
sequenceDiagram
participant VC as MyViewController
participant OR as Original Method
participant SW as Swizzled Method
VC->>SW: call viewDidLoad()
SW->>OR: call my_viewDidLoad() (original)
OR->>SW: call viewDidLoad() (swizzled)
SW->>OR: call my_viewDidLoad() (ending up recursive)
结论
方法交换是一个强大的工具,在提升代码灵活性和实现某些特殊功能方面尤为重要。然而,开发者在使用方法交换时需谨慎,以防止因递归调用造成不必要的崩溃。通过理解方法交换的原理和注意事项,我们可以更好地利用这一技术,开发出更加灵活和高效的 iOS 应用。