@Noescape Attribute in Swift 1.2

@noescape attribute in Swift 1.2

@noescape can be used like this:

func doIt(code: @noescape () -> ()) {
/* what we CAN */

// just call it
code()
// pass it to another function as another `@noescape` parameter
doItMore(code)
// capture it in another `@noescape` closure
doItMore {
code()
}

/* what we CANNOT do *****

// pass it as a non-`@noescape` parameter
dispatch_async(dispatch_get_main_queue(), code)
// store it
let _code:() -> () = code
// capture it in another non-`@noescape` closure
let __code = { code() }

*/
}

func doItMore(code: @noescape () -> ()) {}

Adding @noescape guarantees that the closure will not be stored somewhere, used at a later time, or used asynchronously.

From the caller's point of view, there is no need to care about the lifetime of captured variables, as they are used within the called function or not at all. And as a bonus, we can use an implicit self, saving us from typing self..

func doIt(code: @noescape () -> ()) {
code()
}

class Bar {
var i = 0
func some() {
doIt {
println(i)
// ^ we don't need `self.` anymore!
}
}
}

let bar = Bar()
bar.some() // -> outputs 0

Also, from the compiler's point of view (as documented in release notes):

This enables some minor performance optimizations.

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

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.

How to add @noescape annotation to optional closure

Requirements

If your requirements are the following:

  • the baz param is a closure
  • the baz param is marked with @noescape (because you want to omit self in the closure code)
  • the baz param can be omitted during the invocation of foo

Solution

Then you can use the following syntax

func foo(bar: String, @noescape baz: ((String) -> ()) = { _ in } ) {

}

As you can see the main difference from your code is that:

  • here baz is not an optional type (but it's an "optional parameter")
  • and its default value is an empty closure not a nil value.

Examples

As you requested you can now pass a closure to baz without the need of using self

class Boo {
let world = "world"
func boo() {
foo("hello") { (something) -> () in
print(world)
}
}
}

And you can also omit the baz param

class Boo {
let world = "world"
func boo() {
foo("hello")
}
}

Update: using a closure with return type different from Void

In a comment below users TadeasKriz asked about how to use this approach with a closure having the return value different the Void.

Here it is the solution

func foo(bar: String, @noescape baz: ((String) -> (Int)) = { _ in return 0 } ) {

}

Here the baz param does required a closure with 1 param of type String and a return value of type Int.
As you can see I added a default value to the param, a closure that does return 0. Please note that the default closure will never be used so you can replace 0 with any Int value you want.

Now you can decide whether to use pass your closure to the baz param

class Boo {
let world = "world"
func boo() {
foo("hello") { (something) -> Int in
print(world)
return 100
}
}
}

Or, again, you can totally omit the baz param.

class Boo {
let world = "world"
func boo() {
foo("hello")
}
}

Why does Swift 1.2 reduce force me to use the combine: argument name?

I filed a bug report on this, and Apple replied that the change was intentional. In my view, being able to say reduce(0,+) was elegant and pithy, so the API for reduce should declare the external parameter name unnecessary. In Apple's stated view, the combine: external parameter name clarifies the purpose of the parameter. We have agreed to disagree.

Swift 1.2 - Error caused by closure parameters of a class function

This seems to be a bug in Swift 1.2 (and even Swift 2 (Xcode 7 beta 3)) with single parameter closures with external names.

As workaround you can remove the external parameter name "u":

internal class func closureFunc(#arg: T, worker: (first: T, second: String) -> U, closure: (U -> Void)?) -> Void

in Swift 2 (only) there is another workaround where you can give the passed closure an external name (since this would conform to the new and more strict naming conventions):

Test.closureFunc(arg: "", worker: { (first, second) -> Void in
//code
}) { (anyExternalNameWorks u: Void) -> Void in
//code
}

Create a forCount control structure in Swift

There are no preprocessor macros in Swift, but you can define a global function taking the iteration count and a closure as arguments:

func forCount(count : Int, @noescape block : () -> ()) {
for _ in 0 ..< count {
block()
}
}

With the "trailing closure syntax", it looks like a built-in
control statement:

forCount(40) {
print("*")
}

The @noescape attribute allows the compile to make some optimizations
and to refer to instance variables without using self, see
@noescape attribute in Swift 1.2 for more information.

As of Swift 3, "noescape" is the default attribute for function
parameters:

func forCount(_ count: Int, block: () -> ()) {
for _ in 0 ..< count {
block()
}
}

This is one thing I do not understand in Swift

This was the only form I was able to type this without Xcode complaining. If this is correct, what is the purpose of this ->Void there? What could this possibly returning?

It is the same as in your typealias, in Swift a function type has the form:

(parameter definitions) -> return type

and functions which return nothing have a return type of Void (similar to C). The full form off a closure expression is:

{ (parameter definitions) ->return typeinbody}

Without any inference this expression provides the full type of the closure, and the -> Void Return type in your example specifies that your closure returns nothing. In your assignment this full type will be checked at compile time to conform to the type of onClick.

Now Swift will infer lots of stuff and there are various shorthands available for closure expressions, you will find that Swift accepts:

button.onClick = { textField in }

as well here with both the argument and return types of the closure being inferred.

By the way, as a bonus, [...] any ideas?

Just make the types match:

func doSomething(textField : NSTextfieldSuper) { }
button.onClick = doSomething

Unlike in (Objective-)C functions and closures (blocks in C) are interchangeable (as they are in plenty of other languages, C is the oddfellow here)

HTH



Related Topics



Leave a reply



Submit