Daniel
Daniel

Reputation: 23359

Capture self in block (retain cycles), not always?

The following code is from the LazyTableImages sample code provided by Apple (source here).

In their completion block they have a reference to self which should cause a retain cycle... But I don't get a warning for this in Xcode whereas in similar code of mine I would.

Is this correct?

Perhaps I'm missing a subtlety of this.

- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
    IconDownloader *iconDownloader = [self.imageDownloadsInProgress objectForKey:indexPath];
    if (iconDownloader == nil) 
    {
        iconDownloader = [[IconDownloader alloc] init];
        iconDownloader.appRecord = appRecord;
        [iconDownloader setCompletionHandler:^{

            UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];

            // Display the newly loaded image
            cell.imageView.image = appRecord.appIcon;

            // Remove the IconDownloader from the in progress list.
            // This will result in it being deallocated.
            [self.imageDownloadsInProgress removeObjectForKey:indexPath];

        }];
        [self.imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
        [iconDownloader startDownload];  
    }
}

Upvotes: 6

Views: 1456

Answers (4)

Darren
Darren

Reputation: 25619

There's no warning because the compiler isn't yet capable of detecting all possible retain cycles.

For example:

- (void)foo 
{
    _block = ^ { [self done]; }; // Warning: Possible retain cycle

    DSGenericBlock foo = ^ { [self done] }; 
    _block = foo;  // No warning.
}

If you were to assign the block directly to an instance variable of "self", you would get the "possible retain cycle" warning. Instead, the block is assigned to another object, which is then retained by self, so the compiler does not detect the cycle (even though the cycle does exist).

Upvotes: 0

Abizern
Abizern

Reputation: 150605

The retain cycle that you think you are seeing is because the object holds the the downloader in a dictionary.

It's true that there is a strong reference to self in the block, but, as long as the completion handler is always run, the downloader will be removed from the dictionary. And eventually this dictionary will be empty, which means there will be no objects holding on to self, and thus no retain cycle.

Upvotes: 4

Amin Negm-Awad
Amin Negm-Awad

Reputation: 16650

Capturing self itself is no retain cycle. It is a single reference. One reference cannot build a cycle. The usual antipattern is, that additionale a reference to the block is stored in a strong property of self. Than there are two references building a cycle.

Upvotes: 0

Aaron Brager
Aaron Brager

Reputation: 66242

self doesn't have a strong pointer to iconDownloader. It's created and scoped just to this method:

IconDownloader *iconDownloader = [self.imageDownloadsInProgress objectForKey:indexPath];

If iconDownloader was a strong property (self.iconDownloader) then Xcode would detect a strong reference cycle.

Upvotes: 0

Related Questions