11

Yesterday my latest iOS build ran free of warnings on Xcode. Following an upgrade to Version 9.3 (9E145) overnight I got multiple warnings. When I tried self->score following an answer (1) to a similar question the warnings disappeared.

But in a more recent answer (2) to the same question the problem is solved by altering settings. Currently my settings for Apple LLVM 9.0 - Warnings -Objective C and ARC are

Implicit retain of ‘self’ within blocks Yes

But I don’t understand what Block implicitly retains 'self' means in the context of the code below so I can't say whether or not this behaviour is ‘intended’. Or whether I solved a problem or simply hid it. Or whether answer 1 would be better than answer 2.

Could someone kindly explain what Block implicitly retains 'self' means in this context ? Thanks.

score.alpha = 1.0;

if (sequenceState >= STATES_Count)
{
    [GraphicScore animateWithDuration:8.0f
                                delay:1.0f
                              options:UIViewAnimationOptionCurveEaseOut
                           animations:^{self->score.alpha = 0.0;} // animations:^{score.alpha = 0.0;}
                           completion:^(BOOL finished){ }];
}
[self addSubview:score];
Greg
  • 1,750
  • 2
  • 29
  • 56
  • 4
    It's not that `self->score.alpha` is _better_ than `score.alpha`—technically, it's the exact same code—it's that it makes it obvious that the block is implicitly retaining `self`. This is often a very subtle problem to identify. Writing `self->property` makes it more obvious, and thus makes it easier to spot this (potential) problem in your code. It's like when people write `(void)SomeFunction();` to make it _obvious_ that a return value is being intentionally ignored. – James Bucanek Mar 31 '18 at 01:48

2 Answers2

23

This warning about implicit references to self is useful because in the absence of that, when glancing at code it isn't always obvious which blocks have the risk of introducing strong reference cycles and which don't. By encouraging the programmer to make these self references explicit (such as is required in safe programming languages like Swift), you end up with code where you can plainly see whether a strong reference cycle is a potential issue or not.

So, I'd encourage you to leave the warning turned on but go ahead and make those implicit self references (the ivars) explicit with self-> or, if using properties, self., as advised by the first answer you referenced.

Then you can review those individual uses of self closures and make sure they don't introduce any real risk of strong reference cycles. And if they do, you can adopt the weakSelf or weakSelf/strongSelf patterns as appropriate.

Malloc
  • 15,434
  • 34
  • 105
  • 192
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 1
    Rob, this clarifies things enormously. And thanks fo the references. – Greg Mar 31 '18 at 02:27
  • 2
    Hey @Rob, do you have any resources you could point me to to better understand when these self references introduce retain cycles? I understand the concept of a retain cycle - two objects have a strong reference to each other, so even though you removed your references to those objects, they keep each other from deallocating. I'm having a hard time figuring out exactly how this works with blocks and to stay out of danger. After updating project settings to show this warning, I got 300 warnings, most due to using IVars within API call completion blocks, which are within instance methods. – Jake T. Apr 10 '18 at 20:27
  • 2
    For Swift discussion, see [The Swift Programming Language: Automatic Reference Counting](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID48), and specifically, the section titled [Strong Reference Cycles for Closures](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID56), as well as the section after that, about resolving these cycles. – Rob Apr 11 '18 at 19:35
  • 3
    @JakeT. - The Swift concepts are similar to the Objective-C ones, and those references provide a fairly extensive discussion on the topic. But, if you're looking for Objective-C documentation, see [Programming with Objective-C: Working with Blocks: Avoid Strong Reference Cycles when Capturing self](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW16). – Rob Apr 11 '18 at 19:42
  • Thanks @Rob. I read through the Swift docs there. The bit on lazy var instantiation was helpful, as that's a pattern I picked up but I reference `self` a lot in them. I'm sure I have a lot of strong reference cycles I can break with a simple `[unowned self] in` line being added. Now I'm only stuck on what to do with closures that are passed as completion handlers to asynchronous API methods. These most frequently occur in instance methods on my VCs and objects. The asynchronous API calls are a mix of class methods and instance methods. My limited understanding now is telling me that the – Jake T. Apr 11 '18 at 20:21
  • class method ones are safe, because it's not being referenced by self (self being my VC, say). But my instance methods I may be in danger of? Because say VC owns APIObject, and I have a VC instance method that calls `APIObject.asyncFunctionWithClosure()`, if I reference self in there, I've now got VC -> APIObject -> Closure -> VC – Jake T. Apr 11 '18 at 20:24
  • Ahh, for reference, most of this is actually in Obj-C. I started my project in Obj-C and have switched to mostly writing new stuff in Swift, as I strongly preferred it once I tried it out. – Jake T. Apr 11 '18 at 20:25
  • Worth noticing that when using `weakSelf`/`strongSelf` pattern with dereferencing ivars (`->`) one must check that `strongSelf` is not `nil` (or switch to dot notation). Dereferencing `nil` `strongSelf->_score` will result in `EXC_BAD_ACCESS` crash, while previous version with `_score` would not (potentially introducing retain cycle). – Roman B. Aug 11 '19 at 23:26
  • Roman, yes, that’s the whole point of the `weakSelf`/`strongSelf` pattern. – Rob Aug 12 '19 at 03:41
3

Given:

- (void)bobIsYourUncle {
   ^{score.alpha = 0.0;}();
}

Where score is an instance variable that instance variable is accessed via self. Because the block may be equeued somewhere and executed later, the block retains self when the block is created.

Because ivar access invisibly dereferences through self, that implicit retain is kinda not apparent in the code. The rather ugly suggestion by the compiler of adding self-> in front of the ivar makes it, at least, obvious that self is captured by the block.

So, yes, it is the correct behavior. And, yes, it is sometime desired. You could avoid it by grabbing the ivar's value before the block is created (in a local variable), but you must also know that doing so changes the time at which the ivar's value is obtained and that could cause a behavior change.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • `self->` in front of the ivar is ugly in what sense ? it did work even though Xcode grizzled in one or two places – Greg Mar 31 '18 at 00:21
  • @Greg - The `->` pattern for dereferencing ivars works, but using properties (with dot notation) has been encouraged by Apple for years (see the WWDC 2012 videos [Modern Objective-C](https://developer.apple.com/videos/play/wwdc2012/405/) and [Migrating to Modern Objective-C](https://developer.apple.com/videos/play/wwdc2012/413/)) and tends to be preferred by many. It's a more contemporary pattern, can be used with weak references, graceful handles `nil` references, etc. The `->` feels a tad anachronistic to many of us. – Rob Mar 31 '18 at 02:08
  • @Greg It is ugly to us ObjC greybeards that have been banging brackets since the late 80s. ;). In this modern day of blocks, a compiler double checking my every move and ARC, I've honestly embraced the `->`. What Rob said is absolutely correct, though the compilers recommended fix of `self->` is done explicitly because it is guaranteed not to change runtime behavior whereas switching from direct to access-via-method-call might. – bbum Mar 31 '18 at 16:38