Search code examples
objective-cmemory-managementobjective-c-blockssynchronizedreference-cycle

Does @synchronized(self) in a block lead to a retain cycle?


Let's say I want to do @synchronized(self) within a block. I suppose this will lead to a retain cycle, so normally we would re-write it like this:

-(void)myMethod
{
    __weak TheClass * weakSelf = self;
    dispatch_async(dispatch_get_main_queue(),
    ^{
        TheClass * strongSelf = weakSelf;
        if(strongSelf == nil)
        {
            return;
        }

        @synchronized(strongSelf)
        {
            //mutex code
        }
    }
}

My question is, when you use the @synchronized directive this way, is it equivalent to @synchronized(self)?


Solution

  • Short answer: No

    Longer answer:

    Background

    For there to be a cycle involving a block the block must reference another object, and that object must (directly or via a longer chain) reference the block.

    A cycle in of itself is not bad, it is only bad if it results in the lifetime of objects in the cycle being extended past the point those objects are required. It is fine to create a cycle as long as cycle is broken - by breaking one of the links forming the cycle - at some point.

    A construct like:

    __weak TheClass * weakSelf = self;
    ...
    self.blockVar = ^{
        TheClass * strongSelf = weakSelf;
        ...
    

    prevents a static cycle being created as (the object referenced by) self strongly references (the object referenced by - you get the idea, the variable isn't important but the thing being referenced by it) blockVar but blockVar only has a weak reference back to self.

    However every time the block is executed it creates a strong reference (stored in strongSelf) to self and so creates a dynamic cycle - which is automatically broken when the block finishes executing.

    Your code

    1. Look at your code, you create a block and pass it directly to dispatch_async - you never store a reference to the block in self. So there never is any cycle here, no need to mess with weak references at all.

    2. Once the block creates strongSelf there is a cycle, then using @synchronized(strongSelf) doesn't create a second one, it just takes a lock on the object. When the synchronized statement exits the lock goes, when the block exits the strong cycle goes.

    HTH