Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Swift 3 need @escaping annotation at all?

I read this question and answers and Cocoacasts blog post and I fully understand what is @escaping annotation.

But honestly I do not understand why we need it at all.

The above Cocoacasts blog post states that:

There are several benefits to make closures non-escaping by default. The most obvious benefits are performance and the ability for the compiler to optimize your code. If the compiler knows that a closure is non-escaping, it can take care of a many of the nitty-gritty details of memory management.

But Swift compiler can determine if @escaping is missing and shows an error in this case so if we remove @escaping annotation from Swift language then the compiler still can see when closure does not escape and we can make him apply optimizations in that case.

This also means that you can use the self keyword without issues in non-escaping closures because the closure is invoked before the function returns. There is no need to use a weak reference to self in the closure. This is a nice benefit you get for free.

But if closure parameter is marked as @escaping I can still pass closure that use strong reference to self and compiler does not show any warnings. Actually it would be more useful if all references captured in @escaping closure were weak by default and special keyword were applied to make them strong.

Also I thought that may be @escaping annotation is the way to make code self-documented by explicitly declaring that this closure parameter will not escaping the function body but what the purpose of that for the calling side? It does not restrict the way the closure defined and does not prevent the calling side from using strong references. So all I have left is the hope that the calling side will carefully look at function signature and will take appropriate actions (like using weak references).

So the question is why do we really need @escaping closure in Swift 3 and what are the cases where we could not do without it?

UPDATE:

I know that not escaping closure can not be passed to function which closure parameter marked as @escaping:

func testNoEscape(f: () -> ()) {
    f()
}

var storeF: (() -> ())?

func testEscaping(f: @escaping () -> ()) {
    storeF = f
}

func tryPassNoEscapeToEscaping(f: () -> ()) {
    testEscaping(f: f)
}

results in compilation error:

passing non-escaping parameter 'f' to function expecting an @escaping closure

But that's the only real restriction that @escaping closure brings and it looks like built around itself and does not give any other benefits.

UPDATE 2

Though I laid out my thoughts above correctly but my final question was inaccurate.

The real question is why do we need @escaping annotation if compiler can detect escaping closures by himself and @escaping annotation does not apply any restrictions to parameter values?

In my opinion it would be much more useful if compiler did not allow us to do some bad things in escaping closures like using self and other strong references. Or if escaping closures were some special type and we'd have to mention that when calling function with escaping closure parameter:

func f(c: () -> ()) { // c is escaping from f somehow
    // ...
}

f escaping { // have to use `escaping` keyword 
    // ...
}

So the calling side does not have to look at f signature to know that c is escaping because it will get compilation error if it tries to pass non-escaping closure as escaping closure parameter value.

In current implementation developers who want to use f in their code have to look at f signature to understand that c will escape which is not safe because that requires that anyone who write this code initially and modify it later must know f signature in details which is not reliable and such code is not self-documented.

I understand that maybe my question is not suitable for SO. Sorry about that.

If so I will close it later if I will not get answer from people who implemented escaping logic in Swift language and compiler.

like image 712
mixel Avatar asked Mar 01 '17 11:03

mixel


1 Answers

Non-escaping closures can do things that escaping closures cannot. They are quite different animals.

For example, a non-escaping closure can refer to a property of self without explicitly saying self. This is because, being non-escaping (i.e. it is executed immediately upon receipt), it is in no danger of capturing self in some tricky way and causing a retain cycle.

And, non-escaping closures can close over an inout parameter. But this would be meaningless for an escaping closure and is disallowed.

It would be very mysterious if a closure seemed sometimes to require self and sometimes not to require self, sometimes to permit closure over inout and sometimes not. The @escaping annotation makes such rule distinctions clear and consistent.

like image 117
matt Avatar answered Sep 30 '22 12:09

matt