Swift - @escaping and capture list clarification

741 views Asked by At

I tried to dig on this subject as much as i could, but still i've a few things that wasn't settled down in my head and i will be grateful to get clarification about them.. so i made a few questions..

  1. How the compiler know that i've to add @escaping on background thread, and by that to force me to use it?
  2. Is there a cost for using @escaping? if not.. Why not to mark always? What could went wrong if i mark closure with @escaping even if i don't really need it? When i was trying to do so.. there were no problems on my code and the result with or without @escaping remained the same.
  3. What is the cost of using capture list [weak self] [unowned self]? I know that it making a copy of that object, so i guess temporarily it take more memory, but in the end of that use that copy will removed from memory.. so, there is more disadvantages of using it?
1

There are 1 answers

2
kuzdu On

Short answers

  1. Compilers use different processes to analysis code. There are a bunch of lectures on colleges how compilers work. So it's quite difficult to give you a correct answer.
  2. Use what you need. It's quite difficult to asses how much this cost. @escaping and @no-escaping have different use cases.
  3. I don't see disadvantages. Using weak is a good way to prevent retain cycles and memory leaks.

Explanation

1: So how exactly this compiler works I really don't know. But if you like to get a understanding how compilers generally work read some light weight articles like this one

In my idea the compiler will have a lexical analysis, syntax analysis and semantic analysis. So the compiler will detect if you need an escape or not.

2: Closures is a concept in swift that you "Do stuff when things have done". For more detail have a look in the documentation and here

The following information are based a lot on this article what-do-mean-escaping-and-nonescaping-closures-in-swift

In Swift 1 and 2 closures were @escaping by default. Since Swift 3 closures are @no-escaping.

@no-escaping clousures

When you are passing a closure in function’s arguments, using it before the function’s body gets execute and returns the compiler back. When the function ends, the passed closure goes out of scope and have no more existence in memory.

Simply said, it's comfortable memory handling for the developer because he hasn't to care about anything.

@escaiping closures

For @escaping closures there are two use cases:

Storage: When you need to store the closure in the global variable, property or any other storage that exist in the memory past of the calling function get executed and return the compiler back.

Asynchronous Execution: When you are executing the closure asynchronously on despatch queue, the queue will hold the closure in memory for you, can be used in future. In this case you have no idea when the closure will get executed.

From Apple documentation

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. [...] you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

FYI: Closures marks that an operation is asynchronous and not compellingly on the background thread.

3: I don't see disadvantages to use it. Using weak is a good way to prevent retain cycles and memory leaks. You should ignore the potential costs when you get a stable app. But I repeat: Use what you need. With memory leaks it's a tricky thing and often they are hard to find.

See https://stackoverflow.com/a/34566876/4420355 for a really good answer about delegates and memory leaks. There are a lot of further links in this post too. If you read them you will get a good/better understanding how memory leaks will occur and how to prevent them.

And check this post too, it's a little bit similar to yours https://stackoverflow.com/a/46245943/4420355

Edit to 3

questioner:

I guess i didn't get it right.. so what does it mean "capture", how does it really work behind the scene in terms of unowned self? how the closure use self without owning the object? maybe this question need to be separate to stackoverflow

Inspired by the example of the post Can a local variable's memory be accessed outside its scope?

You rent a hotel room. You check out the next morning, lock the door, but "forget" to give back your key. You steal the key! There is no spare key. So the hotel room is locked. The hotel owner can't rent this room anymore. Now there are a lot of guests which steal the key. Sometime every room is actually free but locked.

With a little bit fantasy the tenant is a closure. He rents the room (creates a references to the room instance). He sleeps there (asynchronous operation). He should give back his key but he doesn't.

In my understanding the closure doesn't owning the object. It's a reference between the closure and a property of an instance.

Apple documentation:

A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance. This capture might occur because the closure’s body accesses a property of the instance, such as self.someProperty, or because the closure calls a method on the instance, such as self.someMethod(). In either case, these accesses cause the closure to “capture” self, creating a strong reference cycle.

You can resolve that strong cycle with weak or unowned. In my opionen the images that apple uses to explain the difference between weak and unowned are really good: See Automatic Reference Counting