Objective-C块的生命周期和自动引用计数(ARC)

4
我对ARC下块的生命周期感到困惑。我编写了一个单元测试来演示我的疑惑。
- (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");
}

我猜测这种行为与代码块在堆栈/堆上的存储方式有关。
更新!
将代码块设置为nil并不会像我期望的那样释放它,因为它在堆栈上,并且只有在超出作用域时才会被释放。强制使代码块超出作用域可以解决我的测试问题。下面是更新后的代码。
- (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");
}
1个回答

4

块是在堆栈上创建的,只有在作用域退出时才会被销毁,就像具有析构函数的C++堆栈分配对象一样。这些块不受引用计数的影响,并将忽略retain/release消息。仅在经过复制(通过Block_copy()函数或copy消息)后,它们变成普通的堆分配对象,可以被保留和释放。

在您的示例中,如果您在其前面添加额外的大括号{ },使得断言在块变量作用域结束后执行,则断言应该开始起作用。


太棒了,谢谢。我尝试使用copy,但我相当确定我所做的只是将该块添加到堆栈中,而不是仅仅在堆栈上操作。块在许多方面都像对象引用,这样的东西确实会让你感到困惑。 - CrimsonChris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接