So I am new to iOS and am using trying to have a view controller with a timer that periodically updates the UI. The issue that I am seeing is that I am getting heap corruption, more specifically EXC_BAD_ACCESS KERN_INVALID_ADDRESS error that is caused by objc_retain call.
This error is happening in several places but all within my Timer function and higher on the call stack __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION is being called in each case.
I must be missing a reference or not releasing something properly, here is the code
func scheduleTimer() {
timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(self.timerFunc), userInfo: nil, repeats: true)
}
func timerFunc() {
if let gps = sdlService?.getLatestLocation() {
let clCoor = CLLocationCoordinate2D(locStruct: gps)
self.updateLatestDriverIcon(gps: gps, coor: clCoor)
if isRecording {
self.addNextPathPoint(coor: clCoor)
}
latestCoor = clCoor
}
}
func updateLatestDriverIcon(gps: LocationStruct, coor: CLLocationCoordinate2D) {
if latestCoor == nil {
car = MarkerAnnotation(coordinate: coor, title: carMarker)
mapView.addAnnotation(car!)
latestCoor = coor
mapView.centerOnLatestGPS(animated: false)
markerView.rotation = MathUtils.wrap(gps.bearing, min: 0, max: 360)
} else if coor.isDifferent(to: latestCoor!) {
if isMapFollowingCar {
mapView.centerOnLatestGPS(animated: false)
}
car!.coordinate = coor
markerView.rotation = MathUtils.wrap(gps.bearing, min: 0, max: 360)
}
}
Now this timer function is referencing properties of my view controller, as well as a nested function (updateLatestDriverIcon). I have seen crashes on the mapView.centerOnLatestGPS() func, and multiple places within the markerView.rotation call stack all with the same error codes listed above. What am I missing here?
EDIT: Here is a stack trace from crashlytics. I am using events triggered over an external accessory so I can be attached to the debugger: Stack Trace
So after several weeks of tracking this thing down, we found it was due to an animation on a UIView. Not exactly sure why it was throwing errors where it did, if anyone knows why that would be very helpful! Here is some more info on the architecture:
We had a screen updating a UI at about 10HZ and was driven by a timer using the above code. The animation was done on a UIView subclass that was done off of the main thread which was being rendered into a bitmap context. This was being done at ~30Hz.
The animation code:
UIView.animate(
withDuration: self.animationDuration,
animations: { self.currentGearValue = actualGearValue },
completion: { (isComplete) in /* not sure we need this yet */ })
I haven't tested it but it might be because the animation is overlapped if the previous one isn't finished by the time the next animation gets started.