Implicit retain cycle

Even if changing to weak wouldn’t be possible, would it make sense to improve the fix-it’s on this? It doesn’t feel right that default fixit only adds self, and new developer suddenly has hidden retain cycle in his/her code...

It makes self explicit to increase the visibility of the possible cycle, it doesn’t hide it.

Yup.

self.handler = { bar() }

can be currently fixed to

self.handler = { self.bar() }

while there's a couple of ways to cut the cycle (e.g [weak self]).

For non closure case, there's no such syntax to cut the cycle right now, and maybe we should come up with new syntax for it (e.g. self.handler = [weak self].bar). But even without it, if it can warn and fix-it by inserting self., I think that'd be really helpful.

What I mean is that a beginner programmer doesn’t necessarily know what retain cycles are. So adding ”self.” does indicate that for them. It’s just a way to make a compiler error go away. ”self?.” Is much more explicit indication that one has to pay attention to the lifecycle of the self.

I agree completely, this is by design. Swift has been designed with progressive disclosure in mind. Meaning that beginners won't learn about things like retain cycles until later (whenever that actually is really depends on the developer).

This is not necessarily true though. Let's think about this through a beginners eyes:

If I didn't even know about retain cycles, then a fixit telling me to use an optional of self will make no sense. If I've never seen (or at least understood) strong/weak/unowned references, I would not understand why/how self could be optional. I might just go ahead and add the fixit to get my code compiling, but then I could easily run into undefined behavior when my self object is nil (for whatever reason) and my callback isn't ran because of how optional chaining works. I would have no idea why my code isn't working the way I'd expect it to work.

I do agree that there needs to be a way to be explicit (I like the [weak self].bar idea), but just making developers use an optional version of self will probably confuse beginner developers more than it will help them. For now, there should probably be a fix it to just add an explicit self, exactly like what you'd expect if you'd written your callback inside a closure. In the future, once the beginner has gained some experience and learned a bit about retain cycles, then they will realize what is actually occurring behind the scenes and can remove unwanted retain cycles.

1 Like

Even for developers that know about retain cycle, adding self here does not in any way indicate a retain cycle.

The same code with a ref to a class instead of a method reference does not create retain cycle.

self.foo = self.bar

If foo and bar are not mehods, there is not retain cycle.

I'd like to emphasize, once again, that there is no such thing as a strong or weak reference. There are only strong or weak variables or properties. That is, strongness or weakness is a property of the box that contains the reference, not of the reference itself.

This seductive fallacy tends to infect discussions like this, because it's almost irresistible as shorthand, even amongst experienced developers.

In this case, the self?.bar syntax looks like it would promote the fallacy amongst future, less experienced developers, because it looks like a style of referencing, rather than what it would really indicate — weakly capturing self inside the method bar.

I'm not sure there's a good syntax for this, but if you want something vaguely like what you've been proposing, then maybe x = [weak self] in bar would not be inconsistent with closure syntax, or perhaps even x = bar[weak self].

But it's not an easy problem, because unmodified instance method name foo isn't really the closure you'd like it to be.

This is where I'm now confused. Is self really just a property/variable of the current object that points to itself? To me, it seems/feels like self really is just a reference to the current object (how else do you describe a variable that points to the current object? I'd call that a reference). That being said, I'm more than willing to rephrase my earlier statement to avoid causing confusion with anyone else that comes across this thread. Do you have any suggestions for how I could better clarify that self is not really just a reference?

I'd prefer the [weak self] in bar for consistency with the closure syntax. I also think it reads more clearly than bar[weak self].

It's hard not to get tangled up here. (This is about my 4th attempt to write a reply that … you know … makes sense.)

Conceptually, self is just a local variable of its scope. The problem with this scenario:

self.foo = self.bar

(where foo is an instance property and bar is an instance method) is that the compiler has to construct a closure that invokes bar, and it's the capture of self inside that closure that's treacherous. The self in = self.bar actually has nothing to do with that.

The self on the RHS is just a reference within an expression (self.bar), and so isn't strong or weak. If you're not careful, you end up asking for a "weak reference" to be stored in a "strong variable", which is a conceptual mistake that an earlier version of this discussion committed a few months ago. (If I can find it, I'll link it.)

Sorry, I'm not sure if that's a sensible answer either. :slight_smile:

4 Likes