ARC related crash in iOS when using a block pointer as a member variable

13,813

Solution 1

You want the property to be copy rather than retain:

@property (nonatomic, copy) APICallOKBlock okCB;

Solution 2

You need to copy the block when saving its instance to the class instance.

-(void)addOKBlock:(APICallOKBlock)okBlock
{
    self.okCB = [okBlock copy];
}
Share:
13,813
Greg
Author by

Greg

Updated on June 04, 2022

Comments

  • Greg
    Greg about 2 years

    Can anyone please help with ARC and memory-related crashes?

    I have class LTRequest with a member variable that stores a block pointer:

    typedef void (^APICallOKBlock)(id);
    
    @interface LTRequest : NSObject
    @property (nonatomic, strong) APICallOKBlock okCB;
    @end
    

    The code to initialize that variable:

    -(void)addOKBlock:(APICallOKBlock)okBlock
    {
        self.okCB = okBlock;
    }
    

    I call the block in case of success:

    -(void)apiCallCompletedWithResult:(id)result
    {
        if(self.okCB != nil)
        {
            self.okCB(result);
        }
    }
    

    For reference, the block looks something like this:

    APICallOKBlock okBlock = ^(id result)
    {
        [self handleAPICallCompleted:result];
    };
    

    It calls a function defined in the object that scheduled the api call, however that part is not important. Important is what happens when apiCallCompletedWithResult is called after the api call has been successful. And what happens is that the block gets called but the app crashes shortly afterwards when the request object LTRequest gets deallocated (SomeAppName is our application):

    Thread 6 name:  Dispatch queue: com.apple.root.low-priority
    Thread 6 Crashed:
    0   libobjc.A.dylib                 0x300bad9c objc_release + 12
    1   libobjc.A.dylib                 0x300c6c00 objc_storeStrong + 24
    2   SomeAppName                     0x00055688 -[LTRequest .cxx_destruct] (LTRequest.m:12)
    3   libobjc.A.dylib                 0x300bba66 object_cxxDestructFromClass + 50
    4   libobjc.A.dylib                 0x300bba2c object_cxxDestruct + 8
    5   libobjc.A.dylib                 0x300b99ac objc_destructInstance + 20
    

    I tried to use unsafe_unretained instead of strong to store the block in the LTRequest object:

    @property (nonatomic, unsafe_unretained) APICallOKBlock okCB;
    

    In that case the crash happens exactly in the line with the if:

    Thread 0 name:  Dispatch queue: com.apple.main-thread
    Thread 0 Crashed:
    0   libobjc.A.dylib                 0x300b9eda objc_retain + 10
    1   libobjc.A.dylib                 0x300c6bde objc_retainAutoreleasedReturnValue + 34
    2   SomeAppName                     0x00054f90 -[LTRequest apiCallCompletedWithResult:] (LTRequest.m:84)
    3   SomeAppName                     0x0002f8aa __29-[LTServerProxy makeAPICall:]_block_invoke_0 (LTServerProxy.m:154)
    
    // file LTRequest.m
    
    line 84    if(self.okCB != nil)
    line 85    {
    line 86        self.okCB(result);
    line 87    }
    

    Am I using the pointer to a block properly and how should I call it to not cause any crashes?

    Some info: I am using iOS SDK 5.0, deployment target is 4.0, device iPhone, and ARC is enabled for the whole project.

  • Greg
    Greg over 12 years
    Thanks, that works! Not sure why developer doc consistently mentions only the new strong/weak attributes and fails to remind that some of the older ones may still be needed.
  • Greg
    Greg over 12 years
    Thanks, good suggestion but setting the copy property attribute is more straightforward (no need to copy in each place when the variable is assigned)
  • Josh Vickery
    Josh Vickery almost 12 years
    blog.refractalize.org/post/10476042560/… has some notes on why this is the case, and a link to an even more detailed article.
  • iOS4Life
    iOS4Life almost 11 years
    This saved me! I had an NSNumber that I had accidentally marked as nonatomic, assign instead of nonatomic, strong! Big difference! Thanks!