Escaping Closure and Mutating Self Explained in Swift
Let’s begin by analysing the following code to learn why it throws an error.
struct User {
var name: String
init(_ userName: String) {
self.name = userName
}
mutating func changeUserName(_ newName: String) {
self.name = newName
}
func escapingFunction(_ x: @escaping () -> ()) {
x()
}
mutating func callEscapingFunction() {
escapingFunction {
self.changeUserName("Ijeoma") // Escaping closure captures mutating 'self' parameter
}
}
}
Here, the User struct has a name property of type String, an initializer and three methods.
The first method is a mutating method named changeUserName, it accepts a single parameter of type string. In the body of the method, the name property is assigned to the value passed to the method.
The second method, escapingFunction, accepts a single parameter and the parameter type is an escaping function that returns void.
The third method, callEscapingFunction, is another mutating method and it accepts no parameters. Inside the method, escapingFunction is called and changeUserName is passed as an argument with the value “Ijeoma.”
This code causes the following error:
领英推荐
// error: Escaping closure captures mutating 'self' parameter
The error happens because we’re passing a mutating instance property of a struct as an argument for an escaping function (closure).
As User is a struct, when we mutate its instance properties Swift secretly replaces the instance with a different instance that holds the updated property value.
The error is thrown because the closure will later attempt to execute on an instance that no longer exists — hence the cryptic message, “captures mutating self.”
This error will not occur if User was a class.
class User {
var name: String
init(_ userName: String) {
self.name = userName
}
func changeUserName(_ newName: String) {
self.name = newName
}
func escapingFunction(_ x: @escaping () -> ()) {
x()
}
func callEscapingFunction() {
escapingFunction {
self.changeUserName("Ijeoma")
}
}
}
let myUser = User("Emily")
print(myUser.name) // Emily
myUser.callEscapingFunction()
print(myUser.name) // Ijeoma
Now, the error is gone because Swift classes can be mutated in place.