ObjC: BAD ACCESS when calling ^ {} blocks in later functions?

After this discussion, I ran into a bad access problem;

The cycle has several steps: a, b, c, ... x, y, z:

-(void)cycle:(float)delta{ [self stepA] [self stepB] // etc. [self stepZ] } 

At some point, step x performs the following actions:

 // IRQ is an NSMutableArray // Self is a reference to the engine running the cycles [IRQ addObject:^{ NSLog(@"hello! %@", self); } ]; 

Later, step z should handle all the β€œdelayed” calls:

  for (int i = 0; i < [IRQ count]; i++){ void (^delayedCall)(void) = [IRQ objectAtIndex:i]; delayedCall(); } [IRQ removeAllObjects]; 

Result: EXEC_BAD_ACCESS

Now, if in step x a simple line is added without an object reference, as shown below, step Z works fine:

 [IRQ addObject:^{ NSLog(@"hello!"); } ]; 

The last observation, if the same step adds blocks to the queue AND iterates in turn to execute the blocks, then no problems arise. How an object reference gets "lost" as a step: does the method remain?

I do not really understand in this area and need additional help!

edit: James, just tried the following to avoid this cyle link:

 NSString *userName = @"James"; [IRQ addObject:^{ NSLog(@"hello %@", userName); } ]; 

and it also happens. How does your decision apply to this?

Thanks in advance!

+1
source share
3 answers

When you create a block with the syntax ^{} , it is created on the stack. To save a block for a long period of time (outside the function that creates it), you must copy the block into a heap:

 void (^ myBlock)(void) = ^ { // your block code is here. }; [IRQ addObject:[[myBlock copy] autorelease]]; 

If you use ARC, skip the -autorelease message.

+3
source

The problem is that block objects are created on the stack. You need to copy the blocks to the heap when you expect them to be used after the area in which they were declared is destroyed, and if the block is not copied for you.

Here you pass the object "down the stack" to a method that does not know about blocks. Replace

 [IRQ addObject:^{ NSLog(@"hello! %@", self); } ]; 

from

 [IRQ addObject:[^{ NSLog(@"hello! %@", self); } copy]]; 

and EXC_BAD_ACCESS will disappear at that moment.

In most cases, you do not need to copy the block! A few examples:

  • If you return the block from the method ("up the stack"), ARC will automatically copy it.
  • If you call a method that does not hold the block, the block does not need to be copied because it remains in scope. Example: a block is passed to -[NSArray sortedArrayUsingComparator:] .
  • If you call a method that uses the block later, the method must take responsibility for copying the block, otherwise each caller will need to copy the block. All methods / functions from the Apple libraries that I know of follow this pattern. Example: completion block passed to +[UIView animateWithDuration:options:animations:completion:] .
+3
source

It seems the object you are going into. In your examples: self and userName prematurely freed. This is not the behavior that I expect from blocks. As in my previous answer, I expected the problem to be due to too much hold!

As a test, you can try:

 NSString *userName = [@"James" retain]; [IRQ addObject:^{ NSLog(@"hello %@", userName); } ]; 

This will be a memory leak, but it will help to indicate whether the object will be freed.

This is caused by a "save cycle" when a block saves self and self saves a block.

Try the following:

 __block typeof(self) blockSafeSelfReference = self; [IRQ addObject:^{ NSLog(@"hello! %@", blockSafeSelfReference); } ]; 

If you use ARC, use __unsafe_unretained instead of __block Strike>

0
source

All Articles