通过协议访问属性触发EXC_BAD_ACCESS错误的排查与修复
解决通过协议类型访问类extension实现的属性时的EXC_BAD_ACCESS错误
这个问题我之前也碰到过,本质是Swift协议的动态分发机制和类extension实现之间的适配冲突,我来给你拆解一下原因和解决办法:
错误产生的原因
- 你的协议
AugmentedRealityViewDisplayingOnSceneKit带有where Self: AugmentedRealityView的约束,说明它仅能被AugmentedRealityView的子类实现。 - 你将协议要求的
currentCameraPosition属性放在了AugmentedRealityViewARKit的extension中实现,而非类的主定义体内。 - 在Swift中,类extension里实现的协议成员,不会被自动加入到协议的动态派发表中。当你把实例强转为协议类型并访问该属性时,运行时无法通过协议的虚表找到正确的实现地址,最终触发
EXC_BAD_ACCESS内存访问错误。 - 而直接强转为具体的
AugmentedRealityViewARKit类型时,Swift会直接通过类自身的虚表找到extension里的属性实现,因此能正常运行。
修复方案
这里有两种可靠的解决办法,你可以根据自己的代码场景选择:
方案一:将属性实现移到类的主定义中
把currentCameraPosition的实现从extension移到AugmentedRealityViewARKit的主类体内,这样属性会被纳入类的动态派发体系,协议类型也能正确找到它:
open class AugmentedRealityViewARKit: AugmentedRealityView { // 将属性实现移到类主定义中 var currentCameraPosition: SCNVector3 { return sceneView.pointOfView?.position ?? .zero } } extension AugmentedRealityViewARKit: AugmentedRealityViewDisplayingOnSceneKit { // 这里无需重复实现属性,类主定义的实现已经满足协议要求 }
方案二:使用@objc强制启用Objective-C动态派发
如果你的类继承自NSObject(从命名来看AugmentedRealityView应该是UIView子类,符合这个条件),可以给协议和属性加上@objc标记,强制使用Objective-C的动态派发机制,这样即使在extension中实现属性,协议类型也能正确寻址:
// 给协议添加@objc标记 @objc internal protocol AugmentedRealityViewDisplayingOnSceneKit where Self: AugmentedRealityView { @objc var currentCameraPosition: SCNVector3 { get } } open class AugmentedRealityViewARKit: AugmentedRealityView { } extension AugmentedRealityViewARKit: AugmentedRealityViewDisplayingOnSceneKit { // 给属性实现也添加@objc标记 @objc var currentCameraPosition: SCNVector3 { return sceneView.pointOfView?.position ?? .zero } }
注意:这种方法要求属性的类型是Objective-C兼容的,SCNVector3刚好满足这个条件,如果是自定义的非ObjC兼容类型,建议优先选择方案一。
内容的提问来源于stack exchange,提问作者Damian Dudycz
相关产品推荐
相关产品推荐

