Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying a block

Could some one tell me why the memory address of the localComplete block and the self.block are the same? self.complete's property is set to copy, and just to be sure i also call copy on localComplete when assigning it to self.complete.

- (void) test {

    CompletionBlock localComplete = ^{

    };

    NSLog(@"localComplete - %p", localComplete);

    self.block = [localComplete copy];

    NSLog(@"self.complete - %p", self.block);

    self.block();
}

Here is the output:

2013-10-05 08:39:18.549 TestApp[90703:a0b] localComplete - 0x60b8
2013-10-05 08:39:18.550 TestApp[90703:a0b] self.complete - 0x60b8

As another example I create strings:

// creating string
self.carType = [[NSString alloc] initWithFormat: @"Good%@", @"year"];
NSLog(@"self.carType - %p", self.carType);

// same memory address???
NSString *carInitString = [[NSString alloc] initWithString: self.carType];
NSLog(@"carInitString - %p", carInitString);

// same memory address???
NSString *carCopy = [self.carType copy];
NSLog(@"carCopy - %p", carCopy);

// different memory address
NSString *carInitWithFormat = [[NSString alloc] initWithFormat: @"%@", self.carType];
NSLog(@"carInitWithFormat - %p", carInitWithFormat);

And the output:

2013-10-05 09:45:01.667 TestApp[91103:a0b] self.carType - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carInitString - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carCopy - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carInitWithFormat - 0xa336b70

Why isn't carInitString and carCopy different memory addresses? Optimizations are turned off in the projects build settings.

like image 627
SukyaMaki Avatar asked Oct 03 '22 18:10

SukyaMaki


2 Answers

Concerning your original question, blocks are normally allocated on the stack. Copying a block (either with the Block_Copy function or the -copy method) will move the block on the stack (further calls will just increase the block's retain count)

Block_copy [...], given a block pointer, either copies the underlying block object to the heap, setting its reference count to 1 and returning the new block pointer, or (if the block object is already on the heap) increases its reference count by 1

(source)

So in your example, you may expect different addresses, since the first block is local, whereas the second one is on the heap, BUT since that specific block doesn't make any reference to the surrounding scope, the compiler will mark it as a global block. Global blocks are not allocated on the stack, and are instead at a fixed location in memory.

Copying a global block won't move the block anywhere. It will just increase its retain count, since the object is already on the heap. That's why you don't get two different addresses.

Try to make a block with a reference to the surrounding context and you will have two different addresses (stack and heap).


Now, let's address your question about NSString.

Copying an immutable object can result in a retain as an optimization (I mean, a framework design optimization, the compiler has nothing to do with it), depending on how the class implements the NSCopying protocol.

This is true for many Foundation classes like NSString, NSArray, NSSet...

This is perfectly compliant with the NSCopying protocol specification, as you can read in the documentation:

Your options for implementing this protocol are as follows:

...

  • Implement NSCopying by retaining the original instead of creating a new copy when the class and its contents are immutable.

As noted by Greg Parker in the comments, -[NSString initWithString:] performs the same kind of optimization. If you pass an immutable string as argument, it just retains and return the same string.


This is a useful behavior in a few situations, here's an example: you declare a property

@property (nonatomic, copy) NSArray *anArray;

and you expose it in the interface.

Declaring it as copy is a good practice in order to ensure that the object you are working on doesn't get changed by the client later on. If you just retain it and the client passes in a NSMutableArray you cannot prevent her to manipulate the object.

So copying is good, but it looks like it comes with a price: you are going to copy the object even when you don't need to (i.e. it's immutable).

Thanks to the behavior discussed above, however, you don't pay such price. Sending copy to an NSArray will just retain it, whereas sending it to a NSMutableArray will actually copy it, so in this case is a big win.

like image 105
Gabriele Petronella Avatar answered Oct 05 '22 10:10

Gabriele Petronella


it could be because the copy isn't necessary. you don't capture any variables or anything like that. so perhaps the block is constant. try having it refer to a local or __block variable.

like image 32
nielsbot Avatar answered Oct 05 '22 11:10

nielsbot