The benefit that I can see here is the ability to guarantee memory safety on API level, by way of specifying weak closure members. Previously, there way no conceivable way of knowing that because a closure can essentially capture whatever it wants (even the very object it's stored in), so this would be a deterministic way of resolving *all* circular references caused by closures.
The downside is that it would require some heavy-duty closure capture analysis on the compiler's part, so I'd expect it to be deferred to Swift 5 or something. However, this does play really nicely with the ownership concept that Swift is going for. I say, let's think this one through very thoroughly and write a very very detailed proposal (including details on how would the core team get around implementing this) and see what it looks like before submitting it. > On Jun 10, 2017, at 8:29 PM, Adrian Zubarev via swift-evolution > <[email protected]> wrote: > > Hello Evolution, > > I’d like to pitch a new idea and see where it would go. Recently I tapped > into a small trap and just now realized that even that non-escaping should > have been the default for closures (SE–0103) there is an exception for that. > Apparently generics don’t follow that rule and a closure like > > Optional<() -> Void> or simply (() -> Void)? > > is still escaping by default. But that was the half of the story yet. As we > all know and “love” reference lists inside closures, methods don’t have any > and we have to wrap method calls into a weak referenced closure > > { [weak self] in self.foo() } > > to avoid strong reference cycles. Maybe you already guess it, I accidentally > didn’t and tapped into the land of strong reference cycles yet again on my > journey. > > I’d like to pitch a new way, more like a new type behavior, for closures on > how they could be used differently in order to avoid strong reference cycles > but also providing the ability to use methods without any need to wrap them. > > Here is a simple code snippet using RxSwift, which will recreate my issue: > > import RxSwift > > let test = PublishSubject<Void>() > > class A { > > let disposeBag = DisposeBag() > > func foo() { > test.asObservable() > .subscribe(onNext: self.bar) // The issue is here > .disposed(by: self.disposeBag) > } > > func bar() { print("works") } > } > > let a = A() > a.foo() > > test.onNext(()) // Testing if it works > test.onCompleted() // Some RxSwift stuff > In this case by passing directly the method self.bar we’re capturing self, > which in this situation isn’t our intention at all. To avoid this issue we > can simply wrap the method call into closure: > > .subscribe(onNext: { [unowned self] in self.bar() }) > > (It’s safe to make it unowned because the dispose bag is a member of self.) > > What if we had the ability for weak or unowned closures? By that I don’t mean > weak/unowned references to the closures themselves, because they are also > reference types, but an invalidation behavior for the whole closure based on > the _captured_ references. For instance: > > let closure1: weak (() -> Void)? = { self.doWhatever() } > > let closure2: weak (() -> Void)? = self.doWhatever > > If one would now try to call the closure, first it will check if all the > captured objects are still available or not, if not the whole closure in this > case will simply become nil and won’t execute. In case of unowned closures it > will trap. Furthermore it will support the general meaning of weak/unowned > and will not increase the reference counter for *captured objects*. > > As you have already noticed, in this case the convention is slightly > different because we must carry the behavior directly with the type. > > func subscribe(onNext: weak ((Swift.E) -> Void)?) > > If the way of my thinking is correct this idea _could maybe_ fade out the > very common [weak self] in guard let strongSelf = self … pattern. > > I personally cannot tell all the technical difficulties this idea might have, > but that’s what the evolution list is for, to collaboratively flesh out the > ideas if they are worth it. > > If something like this could be possible it’s probably worth noting that we > might also be able to introduce something like @autoclosure(weak/unowned) to > Swift for consistency. > > > > > -- > Adrian Zubarev > Sent with Airmail > > _______________________________________________ > swift-evolution mailing list > [email protected] <mailto:[email protected]> > https://lists.swift.org/mailman/listinfo/swift-evolution > <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
