Почему этот блок ObjC не выпускает свои захваченные ссылки, когда он выпущен? Провал тестовый блок включен
Я столкнулся с проблемой, когда объект, захваченный в блоке, кажется, не освобождается, даже после того, как все ссылки на объект и блок были установлены в 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
Можете ли вы сказать мне, почему этот тест не проходит?
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];
}