访问OCMock的andDo中的参数时的EXC_BAD_ACCESS

UrK*_*UrK 14 iphone xcode ocunit ocmock ios

我正在尝试使用OCMock的stub和Do方法编写一段代码.

在这种情况下,正在测试UIImageView扩展类.我想检查扩展名为[self setImage:]并使用非零参数(稍后将使用其他图像比较).

使用OCMock的andDo方法时,在块完成后,测试将与EXC_BAD_ACCESS崩溃.

id mockView = [OCMockObject mockForClass:[UIImageView class]];
[[[mockView stub] andDo:^(NSInvocation *invocation)
  {
      UIImage *img;
      [invocation getArgument:&img atIndex:2]; <---- line causing the exception
      somebodySetImage |= (img != nil);

  }] setImage:OCMOCK_ANY];

  [mockView do_something_that_calls_setImage];
Run Code Online (Sandbox Code Playgroud)

我现在发现的唯一解决方案是使用andCall而不是andDo,但这使测试变得复杂.

我可以用andDo避免崩溃吗?

更新 嗯,我会尝试在这里给出一个更好的例子:这是测试代码的新部分:

- (void)testDownloadingThumbnail
{
    PInfo *_sut = [[PInfo alloc] init];

    __block id target = nil;

    id mock = [OCMockObject mockForClass:[NSOperationQueue class]];

    [[[mock expect] andDo:^(NSInvocation *inv)
    {
        NSInvocationOperation *op;
        [inv getArgument:&op atIndex:2];
        target = [[op invocation] target]; /* replacing this line with STAssert does not help either */
    }] addOperation:OCMOCK_ANY];

    [_sut setDownloadQueue:mock];
    [_sut startDownloadingImagesAsync:YES];

    [mock verify];

    STAssertEqualObjects(target, _sut, @"invalid op target");
}
Run Code Online (Sandbox Code Playgroud)

这是经过测试的代码(来自PInfo的单个方法):

- (void)startDownloadingImagesAsync:(bool)isThumbnailImg
{
    NSInvocationOperation *inv;

    inv = [[NSInvocationOperation alloc] initWithTarget:self
                                     selector:@selector(loadThumbnailWorker:)
                                       object:nil];
    [[self downloadQueue] addOperation:inv];
}
Run Code Online (Sandbox Code Playgroud)

从EXD_BAD_ACCESS 退出startDownloadingImagesAsync时代码仍然崩溃.如果我在andDo块中添加一个断点,我看到控件到达这一点并通过getArgument检索正确的对象.

然而,如果我在块中使用getArgument,它会崩溃我试图做的事情.

PS感谢您的帮助.

psh*_*hah 29

我在使用NSProxy的forwardInvocation:方法时遇到了类似的问题.

你能试试下面的吗?

NSInvocationOperation *op; // Change this line
__unsafe_unretained NSInvocationOperation *op; // to this line
Run Code Online (Sandbox Code Playgroud)

或者另一种方法可能是保留NSInvocation的参数:

[invocation retainArguments];
Run Code Online (Sandbox Code Playgroud)

http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html#//apple_ref/occ/instm/NSInvocation/retainArguments

我稍后会尝试添加更详细的解释.

  • 如果没有__unsafe_unretained,getArgument:方法会将对象分配给该地址,但ARC没有任何线索,因为它接受"void\*"参数(不是"id\*").因此,当发生这种情况时,ARC不知道添加-retain.但是,作为一个强类型的局部变量,它*会在超出范围时添加一个版本.因此,对象将被过度释放.最简单的方法是让局部变量为__unsafe_unretained,这样ARC就可以单独使用它. (5认同)