Swift协议类型转换后赋值报错:Cannot assign to immutable expression咨询
问题解析:Swift中协议类型临时值修改属性报错的原因与解决方法
先还原你的代码片段:
protocol AProt { var a:Int { get set } } protocol BProt { var b:Int { get set } } protocol CProt { var c:Int { get set } } class A:AProt, CProt { var a = 1; var c = 3 } class B:BProt, CProt { var b = 2; var c = 30 } var a = A() var b = B() var c = a as CProt // 正常执行 c.c = 123 a = c as! A print (a.c) // 输出 123 (a as CProt).c = 999 // 抛出错误 print (a.c)
报错原因
你遇到的错误核心在于临时协议值的不可变性:
当你执行(a as CProt)时,Swift会创建一个临时的「协议存在容器(existential container)」—— 这是一个包裹了a实例和CProt协议信息的临时值(右值)。在Swift中,临时值默认是不可变的,哪怕协议定义了get set的可变属性,你也无法直接修改临时协议值的属性,因为临时值本身不允许被修改。
而前面var c = a as CProt能正常修改的原因是:你把这个临时协议值赋值给了一个可变变量c,变量本身是可变的,修改c.c本质是修改变量指向的容器内容(由于A是类类型,容器里存储的是实例引用,因此修改会同步到原实例a)。
解决方法
针对这个问题,有两种常用的解决思路:
方法1:将协议值赋值给可变变量后再修改
这是最直接的方案,和你前面成功的写法逻辑一致:
// 先将转换后的协议值赋值给可变变量 var tempProt = a as CProt tempProt.c = 999 print(a.c) // 输出 999
通过可变变量持有协议值,你就可以合法修改其属性,且由于A是引用类型,修改会同步到原实例a。
方法2:将协议标记为类专属协议
如果你的CProt协议只会被类遵循(不会被结构体、枚举等值类型遵循),可以给协议添加AnyObject约束,将其标记为类专属协议:
// 添加AnyObject约束,协议只能被类遵循 protocol CProt: AnyObject { var c:Int { get set } } // 后续代码无需修改,此时(a as CProt).c = 999可以正常执行 (a as CProt).c = 999 print(a.c) // 输出 999
添加AnyObject约束后,Swift会明确这个协议的实例都是引用类型,此时(a as CProt)返回的不再是通用的协议存在容器,而是一个类类型的引用。对于引用类型的临时实例,你可以直接修改其属性(因为修改的是堆上的实例本身,而非临时引用)。
内容的提问来源于stack exchange,提问作者qwerty_so
相关产品推荐
相关产品推荐

