IBOutlet 崩溃 EXC_BAD_ACCESS 即使不是零
IBOutlet crashing with EXC_BAD_ACCESS even though not nil
在一个 UIViewController (rolePageController) 中,我配置了另一个 UIViewController (drawerController) 并从角色页面传递给它 2 个 UIView,这将成为 drawerController 配置的一部分。一旦 drawerController 尝试从 rolePageController 访问 IBOutlet 视图,它就会崩溃并显示 EXC_BAD_ACCESS(代码=EXC_I386_GPFLT)。
在第一个 VC (rolePageController) 中,这里是 IBOutlets:
@IBOutlet var rolePageDrawerView: UIView!
@IBOutlet var rolePageContentView: UIView!
在 rolePageController.viewDidLoad() 中我调用了 drawerController.configureDrawer(...):
override func viewDidLoad() {
super.viewDidLoad()
//other stuff happens here
let drawerController = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateViewController(withIdentifier: "drawerController") as! DrawerViewController
drawerController.configureDrawer(drawerContainerView: self.rolePageDrawerView, overlaidView: self.rolePageContentView)
//other stuff here
}
DrawerViewController 协议定义为:
protocol DrawerViewController where Self: UIViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
这是 configureDrawer(...) 函数的代码:
private var drawerParentView: UIView!
private var overlaidByDrawerView: UIView!
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView) {
self.drawerParentView = drawerContainerView
self.overlaidByDrawerView = overlaidView
}
在调试器中注意到,调用的 drawerController 实例与接收调用的 self 实例不匹配。这是将要调用的实例的地址:
这是我进入调用时实例的地址:
调用前drawerController的地址不是我step调用时self的地址。那永远不应该发生。
我创建了一个简化的项目来重现 https://github.com/ksoftllc/DynamicStackBufferOverflow 的崩溃。
解决方案
解决方案原来是从 DrawerViewController 协议中删除 where 子句。
protocol DrawerViewController where Self: UIViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
将此函数调用从 viewDidLoad 移至 viewWillAppear
drawerController.configureDrawer(drawerContainerView: self.rolePageDrawerView, overlaidView: self.rolePageContentView)
dynamic-stack-buffer-overflow
与递归没有任何关系。这意味着 alloca
缓冲区已溢出。检查 asan runtime source code.
假设堆栈的布局使得您有一个 alloca
缓冲区,后跟一个对象指针——甚至可能是作为参数传递的对象指针之一。
假设 alloca
缓冲区溢出。在 asan 构建中,这会触发 dynamic-stack-buffer-overflow
错误。但在非 asan 构建中,它只是覆盖该对象指针的字节。假设它写入的字节构成了一个地址,该地址未映射到您的进程页面 table.
如果程序试图读取该对象指针并将其存储在别处(例如,在一个实例变量中),它必须增加该对象的引用计数。但这意味着取消对指针的引用——并且指针指向一个未映射的地址。也许这会导致一般性保护错误,Mach calls an EXC_I386_GPFLT
。
如果您发布了 asan dynamic-stack-buffer-overflow
错误的堆栈跟踪以及导致错误的代码的反汇编,将会很有帮助。
找到了有问题的代码,但我不知道为什么这会导致我看到的错误。 drawerController 符合 DrawerViewController 协议,定义为:
protocol DrawerViewController where Self: UIViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
当我删除 Where 条件时,它不再崩溃。
protocol DrawerViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
where 子句对于程序的正确运行实际上不是必需的,因此我将在没有它的情况下继续。
更新
我用 swift.org 提交了一个错误并收到了回复。 Swift 4.2 不支持向协议添加 where 子句,但 Swift 5.0 将支持。此外,@J Doe 在下面发布了一种通过更新 Xcode 工具包来实现此目的的方法。
它真的看起来像 Swift 编译器错误。我简化了您的代码以进行说明:
func foo(_ wow: TestProtocol) {
wow.foo()
}
protocol TestProtocol where Self: NSObject {
func foo()
}
class TestClass: NSObject, TestProtocol {
func foo() {
print("Much wow")
}
}
foo(TestClass())
您可以将此报告为错误。要解决此问题,我建议您不要使用 where 语句或传递类型为 func foo(_ wow: TestClass {
.
的对象
要解决您的问题,运行 它在开发 t运行k 工具链快照上。您可以在这里下载:
转到快照 -> T运行k 开发(主)XCode(所以不是 Swift 5.0)并下载快照截至 12 月 15 日(我从 11 月 30 日拿到了那个,但我相信 12 月 15 日也可以。)
安装工具链后,在 XCode 中转到:File -> Preferences -> Components
和 select 最新的工具链。 现在 运行 没有任何崩溃。
此外,其中 Self: UIViewController
可以缩短为 :UIViewcontroller
(这仅适用于最新的工具链):
protocol DrawerViewController: UIViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
在一个 UIViewController (rolePageController) 中,我配置了另一个 UIViewController (drawerController) 并从角色页面传递给它 2 个 UIView,这将成为 drawerController 配置的一部分。一旦 drawerController 尝试从 rolePageController 访问 IBOutlet 视图,它就会崩溃并显示 EXC_BAD_ACCESS(代码=EXC_I386_GPFLT)。
在第一个 VC (rolePageController) 中,这里是 IBOutlets:
@IBOutlet var rolePageDrawerView: UIView!
@IBOutlet var rolePageContentView: UIView!
在 rolePageController.viewDidLoad() 中我调用了 drawerController.configureDrawer(...):
override func viewDidLoad() {
super.viewDidLoad()
//other stuff happens here
let drawerController = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateViewController(withIdentifier: "drawerController") as! DrawerViewController
drawerController.configureDrawer(drawerContainerView: self.rolePageDrawerView, overlaidView: self.rolePageContentView)
//other stuff here
}
DrawerViewController 协议定义为:
protocol DrawerViewController where Self: UIViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
这是 configureDrawer(...) 函数的代码:
private var drawerParentView: UIView!
private var overlaidByDrawerView: UIView!
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView) {
self.drawerParentView = drawerContainerView
self.overlaidByDrawerView = overlaidView
}
在调试器中注意到,调用的 drawerController 实例与接收调用的 self 实例不匹配。这是将要调用的实例的地址:
这是我进入调用时实例的地址:
调用前drawerController的地址不是我step调用时self的地址。那永远不应该发生。
我创建了一个简化的项目来重现 https://github.com/ksoftllc/DynamicStackBufferOverflow 的崩溃。
解决方案 解决方案原来是从 DrawerViewController 协议中删除 where 子句。
protocol DrawerViewController where Self: UIViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
将此函数调用从 viewDidLoad 移至 viewWillAppear
drawerController.configureDrawer(drawerContainerView: self.rolePageDrawerView, overlaidView: self.rolePageContentView)
dynamic-stack-buffer-overflow
与递归没有任何关系。这意味着 alloca
缓冲区已溢出。检查 asan runtime source code.
假设堆栈的布局使得您有一个 alloca
缓冲区,后跟一个对象指针——甚至可能是作为参数传递的对象指针之一。
假设 alloca
缓冲区溢出。在 asan 构建中,这会触发 dynamic-stack-buffer-overflow
错误。但在非 asan 构建中,它只是覆盖该对象指针的字节。假设它写入的字节构成了一个地址,该地址未映射到您的进程页面 table.
如果程序试图读取该对象指针并将其存储在别处(例如,在一个实例变量中),它必须增加该对象的引用计数。但这意味着取消对指针的引用——并且指针指向一个未映射的地址。也许这会导致一般性保护错误,Mach calls an EXC_I386_GPFLT
。
如果您发布了 asan dynamic-stack-buffer-overflow
错误的堆栈跟踪以及导致错误的代码的反汇编,将会很有帮助。
找到了有问题的代码,但我不知道为什么这会导致我看到的错误。 drawerController 符合 DrawerViewController 协议,定义为:
protocol DrawerViewController where Self: UIViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
当我删除 Where 条件时,它不再崩溃。
protocol DrawerViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
where 子句对于程序的正确运行实际上不是必需的,因此我将在没有它的情况下继续。
更新 我用 swift.org 提交了一个错误并收到了回复。 Swift 4.2 不支持向协议添加 where 子句,但 Swift 5.0 将支持。此外,@J Doe 在下面发布了一种通过更新 Xcode 工具包来实现此目的的方法。
它真的看起来像 Swift 编译器错误。我简化了您的代码以进行说明:
func foo(_ wow: TestProtocol) {
wow.foo()
}
protocol TestProtocol where Self: NSObject {
func foo()
}
class TestClass: NSObject, TestProtocol {
func foo() {
print("Much wow")
}
}
foo(TestClass())
您可以将此报告为错误。要解决此问题,我建议您不要使用 where 语句或传递类型为 func foo(_ wow: TestClass {
.
要解决您的问题,运行 它在开发 t运行k 工具链快照上。您可以在这里下载:
转到快照 -> T运行k 开发(主)XCode(所以不是 Swift 5.0)并下载快照截至 12 月 15 日(我从 11 月 30 日拿到了那个,但我相信 12 月 15 日也可以。)
安装工具链后,在 XCode 中转到:File -> Preferences -> Components
和 select 最新的工具链。 现在 运行 没有任何崩溃。
此外,其中 Self: UIViewController
可以缩短为 :UIViewcontroller
(这仅适用于最新的工具链):
protocol DrawerViewController: UIViewController {
func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}