user5781005
user5781005

Reputation:

Why is not @noescape automatically applied to Swift closures when it is needed?

I wonder why the tag noescape isn't automatically detected and it needs to be explicitly applied.

In fact, it is somehow detected at compile time, because trying to add the @noescape tag to a func with escaping closures results in an error.

So the question is why... why noescape needs to be explicitly added and Apple did not create it to be automatically added when it is needed?

Upvotes: 0

Views: 838

Answers (2)

rickster
rickster

Reputation: 126157

Edit: Swift 3 makes a few changes here:

  • Non-escaping closures are the default. Now you don't have to apply @noescape to your function declarations that take closures as parameters if you want to require non-escaping closures. (Instead, you have to apply @escaping in cases where you do plan to store a closure past the return of your function.)
  • The "escaping-ness" of a closure is now part of the function type declaration. So instead of a parameter that looks like @escaping completionHandler: (Bool, Error) -> Void, it's completionHandler: @escaping (Bool, Error) -> Void.

It's kinda hard to rewrite my whole answer to reflect that, so I'll leave it here for now... read on for the whys behind escaping-ness, just remember to invert noescape/escaping declarations. :) Or read about Escaping Closures in the Swift 3 version of The Swift Programming Language.


@noescape isn't just a hint for compiler optimizations; it's part of the interface that a function declaration presents to callers. Whether a parameter to a function is declared @noescape or allows escaping closures changes how a caller of that function writes the closure they pass as the parameter.

For example, given the function (from SequenceType):

func filter(@noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.Generator.Element]

If I wanted to filter a collection based on some criteria that requires calling a method on self, I know I can safely do that without worrying about whether the closure will capture self and create a retain cycle.

// horribly contrived example
class Foo {
    var things: [Thing]
    func isCurrentlyAwesomeThing(thing: Thing) -> Bool { /*...*/ }
    func thingsThatAreAwesomeRightNow() -> [Thing] {
        return things.filter {
            return isCurrentlyAwesomeThing($0)
        }
    }
}

If filter permitted an escaping closure, the closure that calls isCurrentlyAwesomeThing() would capture self. (And thus require that method to be called with an explicit self. prefix.) And if the implementation of filter actually saved the closure beyond the runtime of that function, there'd be a memory leak because the closure retains self and self retains the array whose filter function received the closure.

When you call a function whose closure parameters aren't declared @noescape, you have to account for that possibility. This is why you see calls that add a [weak self] capture list and a strong redeclaration inside the closure to make sure self doesn't get deallocated during the closure. (Or at least an [unowned self] in cases where you're reasonably certain the closure won't persist longer than self.)

If closure parameters weren't able to be decorated as @noescape (or as non-escaping through the absence of that decorator), you wouldn't know when calling a function that takes a closure whether you have to be careful about what that closure captures.

Upvotes: 2

Padalingam
Padalingam

Reputation: 149

If you use any function parameter as a noescape you can't able store in another closure, you can't able use dispatch_asynch and you can't able to call non-noescape closure from the noescape closure

The scope of the noescape attribute it should be with in the that closure

Upvotes: 0

Related Questions