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

如何用OCMock存根带响应Block的类方法?解决EXC_BAD_ACCESS崩溃

我之前也踩过这个坑!你遇到的EXC_BAD_ACCESS崩溃,主要是两个问题导致的:一是手动解析NSInvocation时容易踩参数索引的坑,二是局部变量successResult的内存管理没处理好,导致block回调执行时它已经被释放了。下面给你两种正确的测试方式:

方式一:用OCMock内置方法(推荐,简洁又安全)

OCMock专门提供了OCMArg invokeBlockWithArgs方法,帮你直接触发方法中的block参数,完全不用手动处理NSInvocation,既避免了索引错误,又解决了内存管理问题:

// 创建测试用的成功结果
HTTPResult *successResult = [[HTTPResult alloc] init];
successResult.success = YES;
successResult.content = @{@"key":@"value"};

// 生成HTTPUtility的类Mock对象
id httpMock = OCMClassMock([HTTPUtility class]);

// Stub目标类方法,直接用invokeBlockWithArgs触发response回调
OCMStub(ClassMethod([httpMock requestWithHTTPMethod:HTTPRequestMethodGet 
                                          URLString:@"testURL" 
                                         parameters:[OCMArg any] 
                                            response:[OCMArg invokeBlockWithArgs:successResult, nil]])).andReturn(nil);

// 这里调用你的被测代码(它会触发HTTPUtility的requestWithHTTPMethod:方法)

// 测试结束后清理Mock(可选,但建议养成好习惯)
[httpMock stopMocking];

方式二:手动处理NSInvocation(适合特殊场景)

如果因为业务需求必须手动解析NSInvocation,那要注意两个关键细节:

  1. 确保参数索引正确:类方法的NSInvocation参数索引从2开始(索引0是类对象本身,索引1是方法SEL),你的目标block是第4个方法参数,对应的索引是5(这个你之前的代码是对的)。
  2. 正确持有block和结果对象:在ARC下,从NSInvocation获取的block默认是__unsafe_unretained的,要赋值给__strong变量避免提前释放;同时要确保successResult在block执行时仍然存活:
HTTPResult *successResult = [[HTTPResult alloc] init];
successResult.success = YES;
successResult.content = @{@"key":@"value"};

id httpMock = OCMClassMock([HTTPUtility class]);

OCMStub(ClassMethod([httpMock requestWithHTTPMethod:HTTPRequestMethodGet 
                                          URLString:@"testURL" 
                                         parameters:[OCMArg any] 
                                            response:[OCMArg any]])).andDo(^(NSInvocation *invocation) {
    // 用__strong修饰block变量,确保它被正确持有
    void(^__strong responseBlock)(HTTPResult *) = nil;
    [invocation getArgument:&responseBlock atIndex:5];
    
    // 确保successResult在回调执行时仍然存活
    HTTPResult *__strong retainedResult = successResult;
    responseBlock(retainedResult);
});

// 调用被测代码...

[httpMock stopMocking];

为什么你的原代码会崩溃?

  • 内存管理问题:successResult是局部变量,如果被测代码是异步触发block,测试方法执行完后ARC会释放它,此时调用response(successResult)就会访问野指针。
  • block持有问题:从NSInvocation获取的block没有被__strong变量持有,可能被提前释放,导致调用时崩溃。

内容的提问来源于stack exchange,提问作者chenfeng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:57:15