I recently had a bug where I should have been using [weak self] instead of the default strong self (writing nothing).
I know this topic is being discussed a lot lately. But I haven't read anything about the changing the default behaviour in swift. When thinking about all the times where I really needed a strong self and the times where using a weak self would be ok too I came to the point that the majority of closures would work fine with weak selfs. This might be because of the domain I developing for or not. I'm not sure.
Wouldn't it be "better" to change the default behaviour to [weak self] and introduce a strong / guard / whatever the community decides in these topics?
There are many valid cases to omit capturing self at all with no risk of memory leaks. For instance if the closure is not retained by anybody. Treating the omission of capture as [weak self] would be a strange choice IMO since it's not the only way to capture self (we also have unowned). Even though I apply weak self to most of my closures, I don't think I would want it as a default.
I just thought that since it's the (at least for me) most used case it would make a sense having it as a default case. For example in languages like Java where you have to write final in front of every variable people tend to not do it, because it's "more work" I guess. Even if there is no reason to have it non-final. I guess a lot of people like to have the "better" choice as a default.
As I said I'm not sure if it is really a better choice. I use it a lot, but of cause there are reasons to have it non-weak too.
So far, I haven't seen any argument that could convince me that the chosen default is better than capturing everything weak when no setting is given - weak is the right choice in many situations, and yet it's the most tedious option because of the weak/strong dance which is very annoying for many developers.
But apparently the hight priests of Swifthood ;-) had I different opinion, and as changing it now would break compatibility, it's very unlikely that the default will ever be changed.
It might be possible to infer weak capturing when the variable in question is used as an Optional - but this has it's own share of problems, so I think we are stuck with what we have.
I think I pretty profoundly disagree with this point. There are fundamentally two cases in which you capture variables:
Where you are providing a closure that is going to be invoked multiple times as part of a peer object (basically the delegate pattern).
Where you are providing a closure that is going to be invoked once, as a callback. Think of things like DispatchQueue.async.
Consider the failure modes of changing the default capture strength on each case. The status quo is that each case captures strongly. In the case of (1) this can build reference cycles that require manual breaking or will cause object leaks. In the case of (2), this guarantees that all objects that need to be in memory for your DispatchQueue.async invocation will still be in memory at the time of the call.
What if we flipped the default to capture weakly by default instead? The failure mode of (1) goes away: you can't leak memory any more. However, we've introduced a failure mode for (2): as everything captured in an async block is captured weakly, if nothing else keeps those objects alive they will deallocate and vanish. This will lead to fairly rough-to-debug issues in your code too, where you have to try to discover why you're taking an early exit from an enqueued block.
Neither of these is perfect, but in general I take the view that the default behaviour is pretty clear: if you pass an object into a closure, the language guarantees it will be present unless you say otherwise. Explaining to programmers unfamiliar with the language why all the objects in their closures became Optional for no clear reason would definitely be a source of confusion.
Regardless of containing instance (if there's any) the closures capture local scope variables strongly by default. Replacing this behaviour with the weak capture will introduce unnecessary complexities for accessing everything through optionals and also will be very inconvenient for escaping closures, i.e. those that will be executed after exit from the containing scope.
I don't know why Swift closures are designed that way, however this design echoes how closures are behaving in other languages, so new programmers feel more at home.
Personally, I have different gripe with closures: I want to see non-capturing closure modifier, i.e. when the closure won't allow me to accidentally capture anything from the containing scope. That way closures can be used as local pure anonymous functions, where the purity is enforced.
This would be nice. I'd also like the possibility to mark instance methods as pure, basically making them not instance methods, but just pure functions where visibility is tied and namespaced to a type.
Kinda like mutating func, I'd like pure func. And the compiler would guarantee that only pure functions can be called from another pure function. self would be inacessible from such a function.
The difference is that this failure not only easier to detect, but also only happens as a result of a conscious decision of a developer in the majority of cases: I have to add those question marks, guards or capture lists, or the compiler will complain - those objects aren't deallocated unless that behavior is acknowledged.
The default in this scenario would be "you have to think", whereas the current default is "you leak memory". Looking at how much RAM is wasted by common application nowadays, leaks may seem like something you can just ignore - but I still prefer code with proper memory management.
Alas, as I said: We are stuck with what we have, and so we'll see probably one or two requests for [guard self] every year forever... ;-)