23

C.f. Apple's website page on Swift: https://developer.apple.com/swift/

Are there blocks in Swift like in objective-c? How are they created and called?

How would do an asynchronous request in Swift?

Is it easy to create block related memory leaks in swift? If yes, how would you avoid them?

nicael
  • 18,550
  • 13
  • 57
  • 90
Alexis Pribula
  • 698
  • 1
  • 4
  • 14
  • See also [What language level support (if any) does Swift have for asynchronous programming?](http://stackoverflow.com/questions/24011081/what-language-level-support-if-any-does-swift-have-for-asynchronous-programmin/24201915). – al45tair Jun 13 '14 at 09:48
  • Have a look at this project: https://github.com/duemunk/Async. It tries to simplify sequencing of async blocks. Like the async keyword in c#. – Leslie Godwin Jul 16 '15 at 06:36

3 Answers3

28

The Swift equivalent of an (Objective-)C block is called a closure. There's a whole chapter about them in The Swift Programming Language book.

Depending on the context where you use a closure, you can declare/use it with very concise syntax. For example, a method that takes a completion handler whose signature is (success: Bool, error: NSError) - > Void can be called like this:

someMethod(otherParameters: otherValues, completionHandler:{ success, error in
    if !success { NSLog("I am a leaf on the wind: %@", error) }
})

There's also a trailing closure syntax that reads nicely in cases where a closure essentially provides flow control. And you can drop the parameter names when you want to be really brief (at some cost to readability, but that's okay in some obvious cases like the below). Often a return statement is implicit, too.

myArray.sort { $0 < $1 }
let squares = myArray.map { value in
    value * 2
}    

Swift itself doesn't have anything for asynchronous requests, so you use existing API for that. You can use the trailing closure syntax, though:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // do some async stuff
    NSOperationQueue.mainQueue().addOperationWithBlock {
        // do some main thread stuff stuff
    }
}

In most cases, you don't need to worry about creating reference cycles with Swift closures the way you do with ObjC blocks. To put it simply, the capture semantics are similar enough to "just work" the way you want it to for most stuff, but different enough that the common patterns for block/closure uses (e.g. dispatch to background/main thread and referencing self's properties) don't cause cycles.

Cycles are still possible, though, and there is a solution for them. This answer's a bit long already, so check out Strong Reference Cycles for Closures in the docs for the complete explanation.

rickster
  • 124,678
  • 26
  • 272
  • 326
  • 2
    I'm bummed they didn't provide something in-language to streamline async programming, like monads, ES6 yield/generators, etc. – Sean Clark Hess Jun 03 '14 at 14:28
  • I think I saw something that looked like generators in the standard library. Cmd-click a symbol like `String` and scroll around--there's lots of interesting details in there. – rickster Jun 03 '14 at 14:43
  • It looks like they only use them for iteration though. – Sean Clark Hess Jun 03 '14 at 20:15
  • @rickster, I direct you to this question http://stackoverflow.com/a/20760583/294884 which is now out of date. You and/or Francescu may be able to provide better insight! – Fattie Jun 17 '14 at 09:38
8

Blocks in Swift are called closures. They operate much the same as blocks (though are more flexible, and operate in more places). Reference cycles are possible with closures in Swift, and can be avoided with closure capture lists.

“Swift provides an elegant solution to this problem, known as a closure capture list. However, before you learn how to break a strong reference cycle with a closure capture list, it is useful to understand how such a cycle can be caused”

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l

Ben Gottlieb
  • 85,404
  • 22
  • 176
  • 172
  • 2
    iBooks are great, but they aren't the only way to find Swift docs. The relevant section is [Strong Reference Cycles for Closures](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-XID_61) on the web. – rickster Jun 03 '14 at 03:43
3

As I said in another question, you have many ways offered to pass a block equivalent to function in Swift.

I found three.

To understand this I suggest you to test in playground this little piece of code.

func test(function:String -> String) -> String
{
    return function("test")
}

func funcStyle(s:String) -> String
{
    return "FUNC__" + s + "__FUNC"
}
let resultFunc = test(funcStyle)

let blockStyle:(String) -> String = {s in return "BLOCK__" + s + "__BLOCK"}
let resultBlock = test(blockStyle)

let resultAnon = test({(s:String) -> String in return "ANON_" + s + "__ANON" })


println(resultFunc)
println(resultBlock)
println(resultAnon)

Update: There are 2 special cases to the Anonymous function.

The first is that function signature can be inferred so you don't have to rewrite it.

let resultShortAnon = test({return "ANON_" + $0 + "__ANON" })

The second special case works only if the block is the last argument, it's called trailing closure

Here is an example (merged with inferred signature to show Swift power)

let resultTrailingClosure = test { return "TRAILCLOS_" + $0 + "__TRAILCLOS" }

Finally, as an example:

Using all this power what I'd do is mixing trailing closure and type inference (with naming for readability)

PFFacebookUtils.logInWithPermissions(permissions) {
    user, error in
    if (!user) {
        println("Uh oh. The user cancelled the Facebook login.")
    } else if (user.isNew) {
        println("User signed up and logged in through Facebook!")
    } else {
        println("User logged in through Facebook!")
    }
}
Francescu
  • 16,974
  • 6
  • 49
  • 60
  • I thought this was nicely put together but would have followed it faster if you used 'callingFunctionsName' instead of 's'. – Mountain Man Feb 25 '16 at 20:01