Swift 中递归调用时出现 BAD_ACCESS 错误

6
当我尝试使用Swift时,遇到了一个崩溃的情况,但我仍然没有弄清楚原因。
让我们定义一下:
class TestClass {

    var iteration: Int = 0

    func tick() -> Void{
        if iteration > 100000 {
            print("Done!")
            return
        }
        iteration++
        tick()
    }
}

tick()函数会调用自身,并每次增加iteration。任何如下类型的调用:

let test = TestClass()
test.tick()

在递归次数较小的情况下(在我的iMac上约为50000次),程序会崩溃,并显示EXC_BAD_ACCESS错误:The EXC_BAD_ACCESS

如果我定义一个类似的struct而不是class,就不会崩溃(至少在这些限制下不会)。请注意,当程序崩溃时,仅使用了几MB的RAM。

我还不能解释为什么会崩溃。有人能解释一下吗?callbackStorage的值似乎有些可疑,但我没有找到任何线索。

2个回答

11
在您的程序中,每个线程都有一个被称为“堆栈”的东西。 堆栈是一种LIFO(后进先出)数据容器,具有两个主要操作:push,将元素添加到堆栈顶部;pop,从堆栈顶部移除项目。
当程序调用函数时,它会推送调用该函数的代码位置(称为“返回地址”),有时还会推送一些函数参数,然后跳转到该函数的代码。(函数的局部变量也存储在堆栈上。)当函数返回时,它会从堆栈中弹出返回地址并跳转到该地址。
但是,堆栈有限制大小。 在您的示例中,函数自己调用次数太多,导致没有足够的空间来存储所有的返回地址、参数和局部变量。 这被称为堆栈溢出(这就是该网站得名的原因)。 程序试图写入超过堆栈末尾的位置,导致段错误
当使用结构体时,程序不崩溃的原因很可能是因为类比结构体具有更多的开销,正如 dans3itz所说

感谢您提供非常精确的解释。我一直怀疑这种问题。出于好奇,我测试了struct,发现问题确实比预期更严重。 我已经将正确答案归功于dans3itz,因为他第一个回答了问题,但是您的答案也非常感激。谢谢! - Tom

7
您在这里遇到的运行时错误是堆栈溢出。您将定义修改为使用结构体时不会遇到此问题,并不意味着它不会发生。稍微增加迭代深度,您也将能够通过结构体实现获得相同的运行时错误。由于隐式参数正在传递,因此使用类实现会更快地达到此点。

1
结构体确实有相同的问题,大约是两倍远。谢谢回答! - Tom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接