Daoxin
Daoxin

Reputation: 41

Is block copied when it executes?

I have read this in "Block Programming Topics"

"Each invocation of the block provides a new copy of that variable. These variables can in turn be used as const or by-reference variables in blocks enclosed within the block."

So, I have tested following code.

// Employee.h
@interface Employee : NSObject
@end

// Employee.m
@implement Employee
@end

// main.m
int main() {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    Employee* john = [[Employee alloc] init] autorelease];

    void (^blockTest)(void) = ^ {
        NSLog(@"john = %@", john);
        NSLog(@"john retain count = %ld", [john retainCount]);
    };

    blockTest();

    [pool drain];
    return 0;
}

I have expected "John" retain count will be 2 when blockTest is executed, but result is 1.

Can anyone help me to understand it?

Upvotes: 2

Views: 83

Answers (3)

outis
outis

Reputation: 77450

Your quote is incomplete. If you read what comes before it:

Local variables declared within the lexical scope of the block, which behave exactly like local variables in a function.

you'll see that a more accurate quote is:

Each invocation of the block provides a new copy of [local variables]. These variables can in turn be used as const or by-reference variables in blocks enclosed within the block.

john isn't local to the block, so the quote doesn't apply; it applies to variables defined in the block. Also, "provides a new copy" doesn't mean the copy message is sent to the variable contents, it means that there's a separate variable that has the same name, just as every function call produces a new copy of local variables ("behave exactly like local variables in a function").

typedef int (^IntFunc)(int);
typedef IntFunc (^IntFuncFunc)(int);

int main() {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    IntFuncFunc makeAdder;
    IntFunc add2, add3;

    makeAdder = ^(int x) {
        // 'x' is local to this block. Each invocation provides a new copy of 'x'
        return (IntFunc) [[^(int y) {
            return x + y;
        } copy] autorelease];
    };
    add2 = makeAdder(2);
    add3 = makeAdder(3);
    // add2 and add3 each refer to an 'x', but it's not a shared 'x'        
    NSLog(@"add2(0): %d\nadd3(0): %d\n", add2(0), add3(0));

    [pool drain];
    return 0;
}

Output:

add2(0): 2
add3(0): 3

Upvotes: 1

Richard J. Ross III
Richard J. Ross III

Reputation: 55583

Note: assuming that ARC is disabled for this answer, as in an ARC environment, this changes completely.

When a block captures a variable, it const-copies the pointer to the object that you are using, but not the object itself.

Example:

void copyPointer(NSObject *input)
{
    NSLog(@"I have copied the input's pointer! %@", input);
}

void copyObject(NSObject *input)
{
    input = [input copy];

    NSLog(@"I have copied the object itself! %@", input);

    [input release];
}

Thus, when you call -retainCount, it is equivalent to the retainCount of the outer variable, since there is no methods that are called that could increase the retainCount of the receiver.

Upvotes: 0

Erik
Erik

Reputation: 7751

From Apple regarding retainCount:

Important This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.

Upvotes: 0

Related Questions