能否拦截UIViewController子类所有方法?代理实现崩溃问题求助
问题分析与解决方案
首先,你遇到的崩溃大概率是因为代理对象没有正确实现消息转发机制,或者在alloc交换逻辑里犯了几个常见的错误,我帮你梳理下可能的问题点和修复方案:
常见错误点及修复
1. 代理类未正确实现NSObject协议核心方法
当你用代理对象替代原始UIViewController实例时,系统会依赖NSObject的消息转发机制查找方法实现,如果代理类没处理好这些核心方法,必然会触发崩溃。你需要确保代理类实现以下关键方法:
// 让系统识别代理对象为UIViewController类型 - (BOOL)isKindOfClass:(Class)aClass { return [self.targetVC isKindOfClass:aClass] || [super isKindOfClass:aClass]; } // 检查方法响应能力,转发给原始VC - (BOOL)respondsToSelector:(SEL)aSelector { return [self.targetVC respondsToSelector:aSelector] || [super respondsToSelector:aSelector]; } // 获取方法签名,同样转发给原始VC - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *signature = [self.targetVC methodSignatureForSelector:aSelector]; if (!signature) { signature = [super methodSignatureForSelector:aSelector]; } return signature; } // 核心消息转发,将调用转交给原始VC - (void)forwardInvocation:(NSInvocation *)invocation { if ([self.targetVC respondsToSelector:invocation.selector]) { [invocation invokeWithTarget:self.targetVC]; } else { [super forwardInvocation:invocation]; } }
2. alloc交换逻辑的疏漏
只交换+alloc方法是不够的——因为系统内部alloc会调用+allocWithZone:,同时交换逻辑的顺序也容易出错,正确的实现应该是:
// 封装方法交换工具 static void swizzleClassMethod(Class cls, SEL originalSEL, SEL swizzledSEL) { Method originalMethod = class_getClassMethod(cls, originalSEL); Method swizzledMethod = class_getClassMethod(cls, swizzledSEL); BOOL didAddMethod = class_addMethod(cls, originalSEL, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(cls, swizzledSEL, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } // 在+load方法中执行交换(确保只执行一次) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ swizzleClassMethod([UIViewController class], @selector(alloc), @selector(my_swizzled_alloc)); swizzleClassMethod([UIViewController class], @selector(allocWithZone:), @selector(my_swizzled_allocWithZone:)); }); } + (instancetype)my_swizzled_alloc { return [self my_swizzled_allocWithZone:nil]; } + (instancetype)my_swizzled_allocWithZone:(struct _NSZone *)zone { // 先调用原始逻辑获取真实的VC实例 UIViewController *originalVC = [self my_swizzled_allocWithZone:zone]; // 仅对UIViewController子类创建代理 if ([originalVC isKindOfClass:[UIViewController class]]) { return [[ViewControllerProxy alloc] initWithTargetVC:originalVC]; } return originalVC; }
3. 代理对象的生命周期管理
代理对象只需弱引用原始VC即可,避免循环引用问题,同时要确保原始VC释放后代理不会出现野指针访问:
@interface ViewControllerProxy : NSObject @property (nonatomic, weak) UIViewController *targetVC; - (instancetype)initWithTargetVC:(UIViewController *)targetVC; @end @implementation ViewControllerProxy - (instancetype)initWithTargetVC:(UIViewController *)targetVC { if (self = [super init]) { _targetVC = targetVC; } return self; } // 加上前面提到的NSObject协议方法实现 @end
额外注意事项
- 不要在
init方法中处理代理替换,alloc之后会触发init,此时替换会导致逻辑混乱,务必在alloc/allocWithZone的交换方法中处理。 - 先从自定义简单VC子类测试,验证拦截逻辑正常后再推广到所有子类。
- 如果崩溃日志指向特定方法,检查代理是否正确转发了该方法,或原始VC是否确实实现了该方法。
内容的提问来源于stack exchange,提问作者iOSAddicted
相关产品推荐
相关产品推荐

