在struct
中,如果我们在closure
中使用self
,就会得到Closure cannot implicitly capture a mutating self parameter
的错误提示。比如:
struct Foo {
var bar = 10
mutating func changeBar() {
let closure = {
self.bar = 50 // Closure cannot implicitly capture a mutating self parameter
}
closure()
}
}
并且由于Foo
的类型是struct
,我们也没发在closure
里添加截获列表。那么是不是就必须使用class
了?答案是否定的。有两种方式可以解决这个问题。
closure
增加一个inout
类型的参数
方案一:为struct Foo {
var bar = 10
mutating func changeBar() {
let closure = { (s:inout Foo) -> () in
s.bar = 50
}
closure(&self)
}
}
根据inout
类型的说明,我们知道,实际上这相当于增加了一个隐藏的临时变量,self
被复制,然后在closure
中使用,完成后,再复制回self
。也就是说,这个方法有额外的内存开销。如果是struct
较大的情形,这么做并不划算。
UnsafeMutablePointer<Pointee>
方案二:使用这次采用直接指针的方式对于struct
来进行操作,采用指针的好处是self
不会被多次复制,性能较高。缺点是你需要自行确定你的代码的安全。
struct Foo {
var bar = 10
mutating func changeBar() {
let selfPointer = UnsafeMutablePointer(&self)
let closure = {
selfPointer.pointee.bar = 50
}
closure()
}
}
结论
Closure cannot implicitly capture a mutating self parameter
错误的原因是在进出closure
之后,self
的一致性没办法得到保证,所以编译器默认不允许在struct
的closure
中使用self
。如果我们确定这么做是安全的,就可以通过上面的两种方式解决这个问题。其中,方法二的性能更好一些。
struct Foo {
var bar = 10
mutating func changeBar() {
let closure = {
self.bar = 50 // Closure cannot implicitly capture a mutating self parameter
}
closure()
}
}
注意
这里可以记一下指针和swift
变量之间的关系:
UnsafePointer
对应let
UnsafeMutablePointer
对应var
AutoreleasingUnsafeMutablePointer
对应unowned UnsafeMutablePointer
,用于inout
的参数类型
UnsafeRawPointer
对应let Any
,raw系列都是对应相应的Any
类型
UnsafeBufferPointer
是non-owning
的类型(unowned
),用于collection
的elements
, buffer系列均如此