Search code examples
iosswiftcore-dataexc-bad-access

EXC_BAD_ACCESS code=EXC_ARM_DA_ALIGN


I've tested my code with 3 x iPhone 5's, a 5s, 6, 6s, and 7.

I'm getting the above error on all of the iPhone 5 devices only. No idea what is going on here but perhaps the fact that the 5's are 32bit devices may be a clue?

I'm calling the following method from a viewcontroller class

func startRecording() {
    disableControls()

    CoreDataStack.shared.performForegroundTask { (context) in
            let sessionInfo = SessionInfo(context: context)
            sessionInfo.startTime = Date().timeIntervalSince1970
            sessionInfo.userId = self.config.userId
            sessionInfo.devicePosition = self.config.devicePosition.rawValue
            sessionInfo.deviceType = self.config.deviceType.rawValue
            sessionInfo.deviceNumber = self.config.deviceNumber
            sessionInfo.deviceSide = self.config.deviceSide.rawValue

            do {
                    try context.obtainPermanentIDs(for: [sessionInfo])
            } catch {
                    print("Error obtaining permanent ID for session info record")
                    return
            }

            CoreDataStack.shared.saveViewContextAndWait()

            DispatchQueue.main.async {
                    guard sessionInfo.objectID.isTemporaryID == false else {
                            print("ObjectID is temporary")
                            return
                    }

                    self.recording = true
                    self.statusLabel.text = "Recording..."
                    self.recordManager.start(sessionUID: sessionInfo.uid)
            }
    }
}

The config variable is a simple struct:

struct Configuration {
    var userId: String = "Unknown"
    var deviceType: DeviceType = .phone // enum: String
    var deviceSide: DeviceSide = .notApplicable // enum: String
    var deviceNumber: Int16 = 1 
    var devicePosition: DevicePosition = .waist  // enum: String
}

The CoreDataStack is here:

final class CoreDataStack {
    static let shared = CoreDataStack()
    private init() {}

    var errorHandler: (Error) -> Void = { error in
            log.error("\(error), \(error._userInfo)")
    }

    private struct constants {
            static let persistentStoreName = "Model"
    }

    private lazy var persistentContainer: NSPersistentContainer = {
            let container = NSPersistentContainer(name: constants.persistentStoreName)
            container.loadPersistentStores(completionHandler: { [weak self] (storeDescription, error) in
                    if let error = error {
                            self?.errorHandler(error)
                    }
            })
            return container
    }()

    lazy var viewContext: NSManagedObjectContext = {
            self.persistentContainer.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
            self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
            try! self.persistentContainer.viewContext.setQueryGenerationFrom(.current)
            return self.persistentContainer.viewContext
    }()

    private lazy var backgroundContext: NSManagedObjectContext = {
            let context = self.persistentContainer.newBackgroundContext()
            context.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
            return context
    }()

    func performForegroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
            self.viewContext.performAndWait {
                    block(self.viewContext)
            }
    }

    func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
            backgroundContext.perform {
                    block(self.backgroundContext)
            }
    }

    func saveBackgroundContext() {
            viewContext.performAndWait {
                    do {
                            if self.viewContext.hasChanges {
                                    try self.viewContext.save()
                            }
                    } catch {
                            self.errorHandler(error)
                    }

                    self.backgroundContext.perform {
                            do {
                                    if self.backgroundContext.hasChanges {
                                            try self.backgroundContext.save()
                                            self.backgroundContext.refreshAllObjects()
                                    }
                            } catch {
                                    self.errorHandler(error)
                            }
                    }
            }
    }

    func saveViewContext() {
            viewContext.perform {
                    if self.viewContext.hasChanges {
                            do {
                                    try self.viewContext.save()
                            } catch {
                                    self.errorHandler(error)
                            }
                    }
            }
    }

    func saveViewContextAndWait() {
            viewContext.performAndWait {
                    if self.viewContext.hasChanges {
                            do {
                                    try self.viewContext.save()
                            } catch {
                                    self.errorHandler(error)
                            }
                    }
            }
    }
}

The code is bombing out on the following line in the startRecording method:

try context.obtainPermanentIDs(for: [sessionInfo])

Edit:

I've created a stripped down test application consisting of only the CoreDataStack and a model with one entity with one attribute of type string. I'm still getting the same error on 3x iPhone 5's only. 5s, 6, 6s, 7 all work fine.

It would imply the problem lies with the CoreDataStack perhaps?

Github repo here


Solution

  • A couple of people have asked if I solved this, so here is what I did. This is not really a solution but more of a workaround. It may not be appropriate for everyone but worked for me.

    All I did was remove the line

    try! self.persistentContainer.viewContext.setQueryGenerationFrom(.current)
    

    from the CoreDataStack and the issue went away...