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

能否拦截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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:24:55