Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retain cycle with strong reference to dispatch_queue

In a class, I've declared a thread like:

@property (nonatomic, strong) dispatch_queue_t databaseQueue;

and then I perform an operation this thread like

dispatch_async(self.databaseQueue, ^{
        [self.dao deleteRetries];
    });

Can this potentially create a retain cycle?

AND

the current class holds a strong reference to viewControllerToDismiss and there is a code which looks like:

[viewControllerToDismiss dismissViewControllerAnimated:shouldAnimateDismiss completion:^{

   [self performSomeAction];
}

is this a retain cycle?

like image 659
prabodhprakash Avatar asked Sep 14 '17 07:09

prabodhprakash


2 Answers

It is merely a strong reference to self that is eliminated automatically when the block finishes running and GCD releases the block. Note, this is a strong reference between the queue object itself, the block, and self, but not to databaseQueue. E.g. even if databaseQueue was some local reference that had fallen out of scope after you dispatched but before it ran, you'd still have a strong reference between the queue object, the block, and self.

If you don't want that strong reference at all, use weakSelf pattern:

typeof(self) __weak weakSelf = self;
dispatch_async(self.databaseQueue, ^{
    [weakSelf.dao deleteRetries];
});

You asked:

Please could you elaborate more on "Note, this is a strong reference between the queue object itself, the block, and self, but not to databaseQueue"?

Consider:

- (void)runManyTasks {
    dispatch_queue_t queue = dispatch_queue_create("com.domain.app.foo", 0);

    for (NSInteger i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            [self doSomething];
        });
    }
}

- (void)doSomething {
    [NSThread sleepForTimeInterval:1];
}

Even though I have no references in my code to that local variable, queue, after runManyTasks finishes, if I call runManyTasks, GCD will keep its own strong reference to the actual underlying queue object until all tasks finish, and the queue will keep copies of those blocks until they finish running, and those blocks will maintain a strong reference to self until GCD finishes with all of them (roughly 10 seconds, in this example).


You go on to edit your question and ask:

the current class holds a strong reference to viewControllerToDismiss and there is a code which looks like:

[viewControllerToDismiss dismissViewControllerAnimated:shouldAnimateDismiss completion:^{
     [self performSomeAction];
 }

is this a retain cycle?

For all practical considerations, no. That block is released as soon as the dismissal animation is done, so you generally wouldn't complicate your code with weakSelf pattern here. In fact, that view controller isn't dismissed until the animation finishes, anyway, so there's absolutely nothing gained from weakSelf pattern (other than making code more convoluted).

like image 108
Rob Avatar answered Oct 09 '22 06:10

Rob


First, you have not declared a thread. It’s a queue, that’s something different. (Fortunately, as working directly with threads is a pain.)

You are dispatching a block into the queue. The block retains self and the queue retains the block, which means you do have a retain cycle, since the queue, being a strong property, is retained by self:

self -> queue -> block -> self -> queue -> …

BUT the block should be short-lived, judging by the API. When the block is finished, it will get released from the queue, breaking the retain cycle. So I would not worry about a case like this.

like image 25
zoul Avatar answered Oct 09 '22 05:10

zoul