【问题标题】:Why doesn't this ObjC block release its captured references when it's released? Failing unit test included为什么这个 ObjC 块在释放时不释放其捕获的引用?包括失败的单元测试
【发布时间】:2014-09-22 15:54:23
【问题描述】:

我遇到了一个问题,即在块中捕获的对象似乎没有被释放,即使对对象和块的所有引用都已设置为 nil

为了说明这个问题,我整理了这个非常简单的单元测试,应该通过但没有:

/* Headers */

@interface BlockTestTests : XCTestCase

@end

// A simple class that calls a callback when it's 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

你能告诉我为什么这个测试失败了吗?

【问题讨论】:

    标签: objective-c unit-testing objective-c-blocks xctest


    【解决方案1】:

    您的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];
    }
    

    【讨论】:

    • 啊,是的,当然!感谢您的详尽回复。
    猜你喜欢
    • 1970-01-01
    • 2021-12-09
    • 2021-02-02
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 2010-12-18
    相关资源
    最近更新 更多