Having spend hours trying to figure out why I'm getting a EXC_BAD_ACCESS on rare occasions, maybe someone here is able to spot the problem. In my development setup I saw it happening only once, but in our production setup, which has bugsnag integrated I see it happening a couple times a day.
Backstory; we are using CoreMotion to capture accelerometer data, the samples get pushed into a buffer. Every second we process the last 5 seconds of data and run some analysis. To keep the buffer manageable, before processing we scrub the buffer from any samples that are older than 5 seconds. This is exactly where it goes wrong... Below I'll attach thee part of relevant code, does anyone see why this is happening? As far as I see, there should not be a reason of it triggering?
The exception occurs in the cleanBuffer method, inside the filter statement.
(Sorry for the code blob, tried to reduce it as much as possible)
class Engine: NSObject {
let bufferQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".engine", attributes: .concurrent)
var accelerometerBuffer = [SensorSample]();
var lastBufferProcess: UInt = 0;
var lastAccelerometerSample: SensorSample?;
var processor: Processor = Processor();
var bufferProcessWindow: Int = 1000;
// Called by seperate thread every 50 to 500ms
func checkBuffers() -> Int? {
let time = UInt(NSDate().timeIntervalSince1970 * 1000);
if(lastBufferProcess == 0) {
lastBufferProcess = time;
return nil;
}
let bufferTime = time - lastBufferProcess;
if(bufferTime >= self.bufferProcessWindow) {
lastBufferProcess = time;
bufferQueue.async(flags: .barrier) {
self.accelerometerBuffer = self.cleanBuffer(
buffer: self.accelerometerBuffer,
time: time,
bufferSize: 5000);
}
return self.processBuffer();
}
return nil;
}
internal func processBuffer() -> Int? {
var validCount = false
var accelData = [SensorSample]()
bufferQueue.sync {
let accelCount = self.accelerometerBuffer.count
if(accelCount > 3) {
// at least 3 are needed for interpolation
validCount = true
accelData = self.accelerometerBuffer.map { $0.copy() as! SensorSample }
}
}
if (!validCount) { return nil }
return self.processor.process(accelBuffer: accelData);
}
internal func cleanBuffer(buffer: [SensorSample], time: UInt, bufferSize: Int) -> [SensorSample] {
let boundry = Int(Int(time) - bufferSize);
// EXC_BAD_ACCESS on the .withinBuffer command below
return buffer.filter { $0.withinBuffer(boundry: boundry) }
}
// Called by CoreMotion thread every 10ms
func addAccelerometerSample(x: Double, y: Double, z: Double) {
let sample = SensorSample(x: x, y: y, z: z, time: UInt(NSDate().timeIntervalSince1970 * 1000));
if (lastAccelerometerSample === nil || lastAccelerometerSample!.shash != sample.shash) {
bufferQueue.async(flags: .barrier) {
self.accelerometerBuffer.append(sample);
}
lastAccelerometerSample = sample;
}
}
}
public class SensorSample: NSObject, NSCopying {
public var x = Double(0.0);
public var y = Double(0.0);
public var z = Double(0.0);
public var shash = "";
public var time: UInt = 0;
public init(x: Double, y: Double, z: Double, time: UInt) {
self.x = x;
self.y = y;
self.z = z;
self.time = time;
self.shash = String(x) + ":" + String(y) + ":" + String(z);
}
public func withinBuffer(boundry: Int) -> Bool {
return self.time >= boundry;
}
public func copy(with zone: NSZone? = nil) -> Any {
return SensorSample(x: x, y: y, z: z, time: time);
}
}