6

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];  
    }
}
Daniel
  • 23,129
  • 12
  • 109
  • 154
  • 1
    possible duplicate of [Possible to pass \[self anyFunction\] in blocks without \_\_weak object (iOS 5 + ARC)](http://stackoverflow.com/questions/9003600/possible-to-pass-self-anyfunction-in-blocks-without-weak-object-ios-5-arc), Sorry Daniel :) – Abizern Jul 27 '13 at 18:32
  • You're saying there's no warning because there's more than one level to the cycle, @Abizern? – jscs Jul 27 '13 at 18:35
  • There's no warning because there's no retain cycle. – Abizern Jul 27 '13 at 19:08
  • `self` owns `imageDownloadsInProgress`, which owns `iconDownloader`, which owns its `completionHandler`, which takes a strong reference to `self`, @Abizern. There's a retain cycle. It may not be a problematic one, but it's there. – jscs Jul 27 '13 at 20:06
  • Subtle retain cycle. But in our example here, the completion handler does deal with dequeueing the iconDownloader objects. It is a potential retain cycle depending on code changes. – Daniel Jul 27 '13 at 22:02
  • 2
    Possible duplicate of [Clang - Blocks retain cycle from naming convention?](http://stackoverflow.com/questions/15535899/clang-blocks-retain-cycle-from-naming-convention) - compiler uses *naming conventions* to decide wether to warn about a potential retain cycle or not. – Martin R Jul 27 '13 at 22:58

4 Answers4

4

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.

Abizern
  • 146,289
  • 39
  • 203
  • 257
  • 2
    The compiler uses naming conventions to decide wether to warn about a potential retain cycle or not, compare [Clang - Blocks retain cycle from naming convention?](http://stackoverflow.com/questions/15535899/clang-blocks-retain-cycle-from-naming-convention). – Martin R Jul 27 '13 at 22:55
0

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.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
  • The `iconDownloader` object seems to be owned by `imageDownloadsInProgress`, which is presumably owned by `self`; if the `iconDownloader` also takes ownership of its completion Block, then, yes, this is a strong retain cycle. – jscs Jul 27 '13 at 18:31
  • Good catch. I think there is a retain cycle here, but because it's going through an NSDictionary, Xcode isn't creating warning. – Aaron Brager Jul 27 '13 at 18:32
  • On a side note, using the `IconDownloader` class is a recipe for disaster if your data changes frequently. You shouldn't be using the `indexPath` to determine where to put your image after download if there's any chance your data might be located at a different index path whenever the image is done downloading. – Aaron Brager Jul 27 '13 at 18:44
0

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.

Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
0

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).

Darren
  • 25,520
  • 5
  • 61
  • 71