Properly referencing self in dispatch_async

2019-04-05 19:23发布

问题:

How do I properly reference self in a swift closure?

dispatch_async(dispatch_get_main_queue()) {
    self.popViewControllerAnimated(true)
}

I get the error:

Cannot convert the expression's type 'Void' to type 'UIViewController!"

Randomly I tried:

dispatch_async(dispatch_get_main_queue()) { ()
    self.popViewControllerAnimated(true)
}

and it worked. Not sure what the extra () does! Anyone care to explain? Thanks!

回答1:

This is the same issue as people have run in with these questions:

What am I doing wrong in Swift for calling this Objective-C block/API call?

animateWithDuration:animations:completion: in Swift

Here is the general idea:

From the Swift book: https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Closures.html

One of the optimizations of Closures is:

Implicit returns from single-expression closures

Thus, if you just have one line in your closure, your closure's return value changes. In this case, popViewController returns the view controller being popped. By adding () to the closure, you just made it a 2-line closure and the return value isn't implicit anymore!



回答2:

I answered something like this a couple of days ago for a related issue re: Swift's 'implicit return value for single-expression closure': animateWithDuration:animations:completion: in Swift

In this case, the method popViewControllerAnimated returns the UIViewController that was popped from the stack: (https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html#//apple_ref/occ/instm/UINavigationController/popViewControllerAnimated:)

Even though you aren't explicitly doing anything with the return value of that method, Swift is using that value (the returned UIViewController) as the return value for the closure - however, the closure is expecting a return value of Void.

When you added the extra parens (), you essentially added another line to the closure, and since it is no longer a 'single-expression closure' it no longer implicit returns the popped UIViewController.

The community will eventually settle on a convention for handling this, for now when I have been running into it I have been advocating adding return () to the end of the closure (as it clearly states the intention, (1) short circuiting the 'implicit return' by adding another statement, and (2) explicitly returning what was expected).