简体   繁体   中英

Objective-c block lifetime ARC

I'm confused about the lifetime of a block under ARC. I've written a unit test to demonstrate what is confusing me.

- (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");
}

I'm guessing that this behavior has something to do with how blocks are stored on the stack/heap.

Update!

Setting the block to nil doesn't release the block as I expected because it is on the stack and will not be released until it goes out of scope. Forcing the block to go out of scope fixes my test. Updated code below.

- (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");
}

Blocks are created on stack, and only become destroyed when scope exits, pretty much like C++ stack-allocated objects with destructors. These blocks are exempt from reference counting and will ignore retain / release messages. Only after being copied (via Block_copy() function or copy message) they become normal heap-allocated objects that can be retained and released.

In your example, the assert shall start working if you wrap all the code before it in extra curly brackets { } , so that the assert executes after block variable's scope ends.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM