Why doesn't this ObjC block release its captured links when it is released? Unit test error enabled

I had a problem where an object captured inside a block does not seem to be freed even after all references to the object and the block have been set to nil.

To illustrate this problem, I put together this very simple unit test, which should pass, but not:

/* Headers */

@interface BlockTestTests : XCTestCase

@end

// A simple class that calls a callback when it deallocated
@interface Dummy : NSObject

@property (nonatomic, copy) void(^deallocCallback)();

@end

/* Implementation */

@implementation BlockTestTests

- (void)testExample {
    XCTestExpectation *exp = [self expectationWithDescription:@"strong reference should be deallocated when its capturing block is released"];

    Dummy *dummy = [Dummy new];

    dummy.deallocCallback = ^{
        [exp fulfill];
    };

    void(^capturingBlock)() = ^{
        // Captures a strong reference to the dummy
        id capturedStrongReference = dummy;
    };

    capturingBlock = nil;
    dummy = nil;

    // At this point we would expect that all references to the
    // object have been cleared and it should get deallocated.
    // Just to be safe, we even wait 2 seconds, but it never happens...        

    [self waitForExpectationsWithTimeout:2.0 handler:nil];
}

@end

@implementation Dummy

- (void)dealloc {
    _deallocCallback();
}

@end

Can you tell me why this test fails?

+4
source share
1 answer

capturingBlock (, , , , ). @autoreleasepool, , :

  @autoreleasepool {
    void(^capturingBlock)() = ^{
      // Captures a strong reference to the dummy
      id capturedStrongReference = dummy;
    };
    capturingBlock = nil;
    dummy = nil;
  }

, @autoreleasepool ( exp waitForExpectations...). , , , , . :

- (void)testExample {
  XCTestExpectation *exp = [self expectationWithDescription:@"strong reference should be deallocated when its capturing block is released"];

  @autoreleasepool {
    Dummy *dummy = [Dummy new];

    dummy.deallocCallback = ^{
      [exp fulfill];
    };

    void(^capturingBlock)() = ^{
      // Captures a strong reference to the dummy
      id capturedStrongReference = dummy;
    };
    capturingBlock = nil;
    dummy = nil;
  }

  [self waitForExpectationsWithTimeout:2.0 handler:nil];
}
+2

All Articles