我对 ARC 下 block 的生命周期感到困惑。我已经编写了一个单元测试来演示让我感到困惑的地方。
- (void)testBlock {
NSObject *testObject = [[NSObject alloc] init];
CompletionBlock testBlock = ^{ NSLog(@"%@", testObject); };
XCTAssertNotNil(testObject, @"testObject should not be nil");
__weak NSObject *weakTestObject = testObject;
@autoreleasepool {
testObject = nil;
}
XCTAssertNotNil(weakTestObject, @"testObject should be retained by testBlock");
@autoreleasepool {
testBlock = nil;
}
//THIS IS THE FAILING TEST CASE
XCTAssertNil(weakTestObject, @"testObject should have been released when testBlock was released");
}
我猜测这种行为与 block 在堆栈/堆上的存储方式有关。
更新!
将 block 设置为 nil 不会像我预期的那样释放 block ,因为它在堆栈上并且在超出范围之前不会被释放。强制 block 超出范围修复了我的测试。更新了下面的代码。
- (void)testBlock {
NSObject *testObject = [[NSObject alloc] init];
__weak NSObject *weakTestObject = testObject;
@autoreleasepool {
CompletionBlock testBlock = ^{ NSLog(@"%@", testObject); };
XCTAssertNotNil(testBlock, @"testBlock should not be nil");
XCTAssertNotNil(testObject, @"testObject should not be nil");
testObject = nil;
XCTAssertNotNil(weakTestObject, @"testObject should be retained by testBlock");
//testBlock goes out of scope here
}
XCTAssertNil(weakTestObject, @"testObject should have been released when testBlock was released");
}
最佳答案
block 是在堆栈上创建的,只有在作用域退出时才会被销毁,这很像带有析构函数的 C++ 堆栈分配对象。这些 block 免于引用计数并将忽略 保留
/释放
消息。只有在被复制后(通过 Block_copy()
函数或 copy
消息)它们才成为可以保留和释放的普通堆分配对象。
在您的示例中,如果您将断言之前的所有代码包装在额外的大括号 {
}
中,断言将开始工作,以便断言在 block 变量的范围之后执行结束。
关于Objective-c block 生命周期 ARC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24093802/