升级到Swift 4.0可能遇到的坑总结

2020-01-08 23:47:50于丽

三. 项目中遇到的一些的报错问题

3-1. "Closure cannot implicitly capture a mutating self parameter"错误

在struct中,如果我们在闭包中使用self,就会得到Closure cannot implicitly capture a mutating self parameter的错误提示。比如:


struct RecordModel {
 /// 定义一个闭包
 var action: (() -> ())?
 var height = 10
 
 self.action = { 
  self.height = 20 
  //Closure cannot implicitly capture a mutating self parameter报错
 }
}

++并且由于RecordModel的类型是struct,我们也没发在action闭包里添加截获列表。那么是不是就必须使用class了?答案是否定的。有两种方式可以解决这个问题。++

方案一:为closure增加一个inout类型的参数


struct RecordModel {
 /// 定义一个闭包
 var action: ((_ inSelf: inout RecordModel) -> ())?
 var height = 10
 
 self.action = { (inSelf) in
  inSelf.height = 20 
 }
}

根据inout类型的说明,我们知道,实际上这相当于增加了一个隐藏的临时变量,self被复制,然后在closure(闭包)中使用,完成后,再复制回self。也就是说,这个方法有额外的内存开销。如果是struct较大的情形,这么做并不划算。

方案二:使用UnsafeMutablePointer<Pointee>

==这次采用直接指针的方式对于struct来进行操作,采用指针的好处是self不会被多次复制,性能较高。缺点是你需要自行确定你的代码的安全。==


struct RecordModel {
 /// 定义一个闭包
 var action: (() -> ())?
 var height = 10
 
 let selfPointer = UnsafeMutablePointer(&self)
 self.action = { 
  selfPointer.pointee.height = 20 
  
 }
}

结论

==Closure cannot implicitly capture a mutating self parameter错误的原因是在进出closure(闭包)之后,self的一致性没办法得到保证,所以编译器默认不允许在struct的closure(闭包)中使用self。如果我们确定这么做是安全的,就可以通过上面的两种方式解决这个问题。其中,方法二的性能更好一些。==

注意

这里可以记一下指针和swift变量之间的关系:

UnsafePointer对应let UnsafeMutablePointer对应var AutoreleasingUnsafeMutablePointer对应unowned UnsafeMutablePointer,用于inout的参数类型 UnsafeRawPointer对应let Any,raw系列都是对应相应的Any类型 UnsafeBufferPointer是non-owning的类型(unowned),用于collection的elements, buffer系列均如此

3-2. Declarations from extensions cannot be overridden yet 错误

==这个错误大致是因为,协议方法是在extension里面的,不能被重写==

解决办法:(仅供参考,如有更好的建议还望多多指教)

小编想到的解决办法就是在每一个需要此协议的类里面,重新遵循代理,实现该协议方法