Search code examples
objective-ccocoaappkit

Cocoa - Block implicitly retains 'self' - Different solutions


So I have this snippet here which involves a block:

NSArray<Class> *acceptableClasses = @[[DesktopEntity class]];
    __block NSInteger insertIdx = row;
    
    
    [info enumerateDraggingItemsWithOptions:0 forView:_tableView classes:acceptableClasses searchOptions:nil usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop)
     {
        
        
        DesktopEntity *entity = draggingItem.item;
        
       
        [_tableContents insertObject:entity atIndex: insertIdx];
        [_tableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:insertIdx] withAnimation:NSTableViewAnimationEffectGap];
        
    
        draggingItem.draggingFrame = [_tableView frameOfCellAtColumn:0 row: insertIdx];
        ++insertIdx;
    }];

it compiles fine but I'm getting a warning:

Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior

I'm having some trouble understanding what it means. I managed to make the warning disappear by just typing the self keyword before the variables:

[self.tableContents insertObject:entity atIndex: insertIdx];
[self.tableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:insertIdx] withAnimation:NSTableViewAnimationEffectGap];


draggingItem.draggingFrame = [self.tableView frameOfCellAtColumn:0 row: insertIdx];
++insertIdx;

But I've also seen people using constructs such as

__weak typeof(self) weakSelf = self;

and then using this 'weakSelf' instead, what's the difference here and is my first approach wrong?


Solution

  • The block does retain self the way you're doing it.

    If you know for a fact that the block is not being stored for later use by the runtime, that's fine; but if you cannot be sure that the block is not being stored for later use, you risk a retain cycle: the block retains self but perhaps, behind the scenes, someone is retaining the block, thus preventing self from ever going out of existence.

    And in that case, you should use the "weak-strong dance", as illustrated in the code at the end of your question, which breaks the retain cycle by referring to self with a weak reference.

    But which is the case? Is this block retained behind the scenes or not? Alas, the runtime's behavior behind the scenes is opaque. One way to find out experimentally whether there is a retain cycle is to implement dealloc on self to log to the console, and try running the app with the code your way. If the printing in dealloc never happens, then self is never being deallocated because you've created a retain cycle and you know that the weak-strong dance is needed.

    Personally, I'd be inclined just to do the weak-strong dance and let it go at that. It is over-used in a knee-jerk way by a lot of programmers, so you are right to doubt its necessity here; but it does no harm and can do good, so it might be best to err on the side of caution.