为啥这个 ObjC 块在释放时不释放其捕获的引用?包括失败的单元测试
Posted
技术标签:
【中文标题】为啥这个 ObjC 块在释放时不释放其捕获的引用?包括失败的单元测试【英文标题】: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
你能告诉我为什么这个测试失败了吗?
【问题讨论】:
【参考方案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];
【讨论】:
啊,是的,当然!感谢您的详尽回复。以上是关于为啥这个 ObjC 块在释放时不释放其捕获的引用?包括失败的单元测试的主要内容,如果未能解决你的问题,请参考以下文章
_swift_abortRetainUnowned 将 @objc 类捕获为无主时