Swift 递归闭包栈溢出bug
Swift recursive closure stack overflow bug
更新:需要注意的是,下面的问题是学术性质的,并且是核心位置的使用,或者位置数据的轮询与问题无关——正确的做法总是通过核心位置委托方法。我最初的问题最终归结为:"Is infinite recursion ever possible in swift? (or tail recursion)"。答案是否定的。这就是由于堆栈 space 耗尽导致我的错误的原因。
原问题:
我在使用通过闭包传递值的递归函数时遇到问题。我是 Objective-C 的长期开发人员,但 Swift 的编程时间不长,所以我可能遗漏了一些明显的东西。这是函数以及我如何调用它:
public func getLocation(completion: @escaping (CLLocationCoordinate2D?) -> ())
{
completion(self.currentLocation)
weak var weakSelf = self
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
weakSelf?.getLocation(completion: {location in
completion(weakSelf?.currentLocation)
})
}
}
LocationManager.shared.getLocation(completion: {location in
if(location != nil)
{
weakSelf?.lonLabel.text = "Lat: " + location!.latitude.description
weakSelf?.latLabel.text = "Lon: " + location!.longitude.description
}
})
我在 运行 一段时间后得到的错误(描述 Thread 1: EXC_BAD_ACCESS (code=2, address=0x16f1b7fd0)
)是这样的:
我想要完成的是将自动更新的位置值传递给位置管理器对象。我在想另一种方法是 performSelector withObject afterDelay
但此时,我只是想知道为什么会崩溃?
你导致堆栈溢出,让 getLocation(completion:)
调用 getLocation(completion:)
,调用 getLocation(completion:)
,...直到你 运行 出栈 space.
您可以使用 DispatchSourceTimer
代替:
import Dispatch
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
timer.schedule(deadline: .now(), repeating: .seconds(5), leeway: .milliseconds(100))
timer.setEventHandler { [weak self] in
guard let strongSelf = self,
let location = LocationManager.shared.currentLocation else { return }
strongSelf.lonLabel.text = "Lat: \(location.latitude)"
strongSelf.latLabel.text = "Lon: \(location.longitude)"
}
timer.resume()
但是整个轮询方法没有任何意义。您的位置代表已被告知位置更改。然后你就可以改变你的标签了。
更新:需要注意的是,下面的问题是学术性质的,并且是核心位置的使用,或者位置数据的轮询与问题无关——正确的做法总是通过核心位置委托方法。我最初的问题最终归结为:"Is infinite recursion ever possible in swift? (or tail recursion)"。答案是否定的。这就是由于堆栈 space 耗尽导致我的错误的原因。
原问题: 我在使用通过闭包传递值的递归函数时遇到问题。我是 Objective-C 的长期开发人员,但 Swift 的编程时间不长,所以我可能遗漏了一些明显的东西。这是函数以及我如何调用它:
public func getLocation(completion: @escaping (CLLocationCoordinate2D?) -> ())
{
completion(self.currentLocation)
weak var weakSelf = self
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
weakSelf?.getLocation(completion: {location in
completion(weakSelf?.currentLocation)
})
}
}
LocationManager.shared.getLocation(completion: {location in
if(location != nil)
{
weakSelf?.lonLabel.text = "Lat: " + location!.latitude.description
weakSelf?.latLabel.text = "Lon: " + location!.longitude.description
}
})
我在 运行 一段时间后得到的错误(描述 Thread 1: EXC_BAD_ACCESS (code=2, address=0x16f1b7fd0)
)是这样的:
我想要完成的是将自动更新的位置值传递给位置管理器对象。我在想另一种方法是 performSelector withObject afterDelay
但此时,我只是想知道为什么会崩溃?
你导致堆栈溢出,让 getLocation(completion:)
调用 getLocation(completion:)
,调用 getLocation(completion:)
,...直到你 运行 出栈 space.
您可以使用 DispatchSourceTimer
代替:
import Dispatch
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
timer.schedule(deadline: .now(), repeating: .seconds(5), leeway: .milliseconds(100))
timer.setEventHandler { [weak self] in
guard let strongSelf = self,
let location = LocationManager.shared.currentLocation else { return }
strongSelf.lonLabel.text = "Lat: \(location.latitude)"
strongSelf.latLabel.text = "Lon: \(location.longitude)"
}
timer.resume()
但是整个轮询方法没有任何意义。您的位置代表已被告知位置更改。然后你就可以改变你的标签了。