Skip to content

Crash in NSInvocation -dealloc when using OCMPartialMock #346

Open
@sdefresne

Description

@sdefresne

When using OCMPartialMock to mock a UIView that has been inserted in the view hierarchy (passed to -addSubView: of some other view), then the app crashes in NSInvocation -dealloc. This can be reproduced with the following test:

- (void)testDealloc
{
    UIView* view1 = [[UIView alloc] initWithFrame:CGRectZero];
    UIView* view2 = [[UIView alloc] initWithFrame:CGRectZero];
    [view1 addSubview:view2];
    id viewMock = OCMPartialMock(view2);
    NSLog(@"silence warning: Unused variable 'viewMock', %p", (__bridge void*)viewMock);
}

To test, just paste this in iOS9ExampleTests.m, build with Xcode 9.0, and run the tests on iPhone 6s 10.0 simulator, and you'll have a crash with the following callstack:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x000000010f7d6d2b libobjc.A.dylib`objc_release + 11
    frame #1: 0x000000010fdb920e CoreFoundation`common_removeAllObjects + 254
    frame #2: 0x000000010fc968c3 CoreFoundation`-[__NSArrayM dealloc] + 19
    frame #3: 0x000000010f7d7b12 libobjc.A.dylib`objc_object::sidetable_release(bool) + 212
    frame #4: 0x000000010f7d20c3 libobjc.A.dylib`_object_remove_assocations + 375
    frame #5: 0x000000010f7cd4ea libobjc.A.dylib`objc_destructInstance + 141
    frame #6: 0x000000010f7cd510 libobjc.A.dylib`object_dispose + 22
    frame #7: 0x000000010fce911b CoreFoundation`-[NSInvocation dealloc] + 139
    frame #8: 0x000000010f7d7b12 libobjc.A.dylib`objc_object::sidetable_release(bool) + 212
    frame #9: 0x000000010fdb920e CoreFoundation`common_removeAllObjects + 254
    frame #10: 0x000000010fc968c3 CoreFoundation`-[__NSArrayM dealloc] + 19
    frame #11: 0x000000010f7d7b12 libobjc.A.dylib`objc_object::sidetable_release(bool) + 212
  * frame #12: 0x000000011e9fb2c1 iOS9ExampleTests`-[OCMockObject dealloc](self=0x000064800009b120, _cmd=<unavailable>) at OCMockObject.m:113 [opt]
    frame #13: 0x000000011e9f92e0 iOS9ExampleTests`-[OCClassMockObject dealloc](self=0x000064800009b120, _cmd=<unavailable>) at OCClassMockObject.m:40 [opt]
    frame #14: 0x000000011e9fdda0 iOS9ExampleTests`-[OCPartialMockObject dealloc](self=0x000064800009b120, _cmd=<unavailable>) at OCPartialMockObject.m:44 [opt]
    frame #15: 0x000000010f7d7b12 libobjc.A.dylib`objc_object::sidetable_release(bool) + 212
    frame #16: 0x000000010f7d81d1 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 715
    frame #17: 0x000000011b70e999 XCTest`__24-[XCTestCase invokeTest]_block_invoke + 671
    frame #18: 0x000000011b756f45 XCTest`-[XCUITestContext performInScope:] + 183
    frame #19: 0x000000011b70e6ef XCTest`-[XCTestCase invokeTest] + 141
    frame #20: 0x000000011b70f6b0 XCTest`__26-[XCTestCase performTest:]_block_invoke.369 + 42
    frame #21: 0x000000011b75bc4b XCTest`+[XCTContext runInContextForTestCase:block:] + 163
    frame #22: 0x000000011b70f04c XCTest`-[XCTestCase performTest:] + 608
    ...

This used to work with revision f03b3cc but fails with the most recent revision from github. Using git bisect, I've found that this was introduced by bf6f59a (reland of 982c6f7 with compilation fixes, this version also fails with the same error when the compilation is fixed).

The crash happens in OCMockObject -dealloc method when sending the -release signal to invocations ivar. This release the object side-table used to store the association created by -retainObjectArgumentsExcluding: so I suspect that the bug is present in that method (or maybe the bug was always there bug never visible due to the object cycle this method is trying to prevent).

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions