It is easy to get started with Swift and that is exactly one of the compelling aspects of the language. But as you use the language more often, you gradually come into contact with Swift's more complex constructs.
In Swift 2, you may have run into the @noescape
attribute. Have you ever taken the time to understand what it means? In Swift 3, @noescape
has been removed from the language. Why is that? And why does Swift 3 introduce @escaping
? Answering these questions is the focus of this article.
What Is the Meaning Of @noescape?
Even though @noescape
is deprecated in Swift 3, it is useful to understand what it means. Why is that? The answer is simple. The @noescape
attribute is applied by default in Swift 3. Let us start form the beginning.
What Is an Escaping Closure?
You first need to understand what an escaping closure is. The definition is very simple and easy to understand. If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping. It is also said that the closure argument escapes the function body. With this definition in mind, the name of the term escaping closure is well chosen. Right?
If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping.
In Swift 2, you could mark a function parameter with the @noescape
attribute, telling the compiler that the closure passed to the function is not allowed to escape the function body. Take a look at the following example. Note that this example is written in Swift 2.
import HealthKit
class HealthKitManager: NSObject {
private let healthStore = HKHealthStore()
func requestAuthorization(completion: (Bool, NSError?) -> Void) {
var shareTypes = Set<HKSampleType>()
var readTypes = Set<HKSampleType>()
// Add Workout Type
shareTypes.insert(HKSampleType.workoutType())
readTypes.insert(HKSampleType.workoutType())
// Request Authorization
healthStore.requestAuthorizationToShareTypes(shareTypes, readTypes: readTypes, completion: completion)
}
}
You can see that the requestAuthorization(_:)
function accepts one parameter, a closure. In Swift 2, closures can by default escape the function body and that is why the above example doesn't cause the compiler any issues. Note that the completion
parameter is passed as an argument to the requestAuthorizationToShareTypes(_:readTypes:completion:)
method of HKHealthStore
. This method is asynchronous, which means the closure is invoked after requestAuthorization(_:)
returns. In other words, the closure we pass to requestAuthorization(_:)
is escaping. It escapes the function body of requestAuthorization(_:)
.
Things become interesting if we add the @noescape
attribute to the parameter of requestAuthorization(_:)
. This is what the example looks like with the @noescape
attribute.
import HealthKit
class HealthKitManager: NSObject {
private let healthStore = HKHealthStore()
func requestAuthorization(@noescape completion: (Bool, NSError?) -> Void) {
var shareTypes = Set<HKSampleType>()
var readTypes = Set<HKSampleType>()
// Add Workout Type
shareTypes.insert(HKSampleType.workoutType())
readTypes.insert(HKSampleType.workoutType())
// Request Authorization
healthStore.requestAuthorizationToShareTypes(shareTypes, readTypes: readTypes, completion: completion)
}
}
We explicitly tell the compiler that the completion
parameter should not be allowed to escape the function body. The result is that the compiler throws an error, explaining what is wrong.
Swift 3
Let me show you what the above example looks like in Swift 3. This will show you what has changed and what you need to know about escaping closures in Swift 3.
import HealthKit
class HealthKitManager: NSObject {
private let healthStore = HKHealthStore()
func requestAuthorization(@noescape completion: (Bool, Error?) -> Void) {
var shareTypes = Set<HKSampleType>()
var readTypes = Set<HKSampleType>()
// Add Workout Type
shareTypes.insert(HKSampleType.workoutType())
readTypes.insert(HKSampleType.workoutType())
// Request Authorization
healthStore.requestAuthorization(toShare: shareTypes, read: readTypes, completion: completion)
}
}
The compiler immediately informs us that @noescape
is the default in Swift 3 and suggests to remove @noescape
. In fact, @noescape
is deprecated and you should no longer use it in Swift 3.
Because the closure we pass to requestAuthorization(completion:)
(note that the method signature is different in Swift 3) is escaping, we need to mark the parameter as escaping. We use a new attribute for this, @escaping
. This is a direct result of SE-0103, a Swift evolution proposal that proposes to make non-escaping closures the default. This is a very welcome change if you ask me.
import HealthKit
class HealthKitManager: NSObject {
private let healthStore = HKHealthStore()
func requestAuthorization(completion: @escaping (Bool, Error?) -> Void) {
var shareTypes = Set<HKSampleType>()
var readTypes = Set<HKSampleType>()
// Add Workout Type
shareTypes.insert(HKSampleType.workoutType())
readTypes.insert(HKSampleType.workoutType())
// Request Authorization
healthStore.requestAuthorization(toShare: shareTypes, read: readTypes, completion: completion)
}
}
You may have noticed that the @escaping
attribute precedes the type of the parameter, not the name. This too is new in Swift 3.
What Is the Meaning of @escaping?
This brings us to the meaning of the @escaping
attribute. Because closures are by default non-escaping in Swift 3, escaping closures need to be marked as such. And the @escaping
attribute lets us do that.
The error of the compiler disappears by marking the closure as escaping, using the @escaping
attribute.
Why Is This Important?
There are several benefits to make closures non-escaping by default. The most obvious benefits are performance and the ability for the compiler to optimize your code. If the compiler knows that a closure is non-escaping, it can take care of a many of the nitty-gritty details of memory management.
This also means that you can use the self
keyword without issues in non-escaping closures because the closure is invoked before the function returns. There is no need to use a weak reference to self
in the closure. This is a nice benefit you get for free.
Questions? Leaven them in the comments below or reach out to me on Twitter.