EXC_BAD_ACCESS代码= EXC_ARM_DA_ALIGN

时间:2017-01-07 12:52:38

标签: ios swift core-data exc-bad-access

我用3 x iPhone 5,5s,6,6s和7测试了我的代码。

我只在所有iPhone 5设备上收到上述错误。不知道这里发生了什么,但也许这个5位是32位设备的事实可能是一个线索?

我从viewcontroller类调用以下方法

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)
            }
    }
}

config变量是一个简单的结构:

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
}

CoreDataStack在这里:

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)
                            }
                    }
            }
    }
}

代码在startRecording方法的以下行中轰炸:

try context.obtainPermanentIDs(for: [sessionInfo])

修改

我创建了一个精简测试应用程序,它只包含CoreDataStack和一个带有一个属性类型为string的实体的模型。我仍然只在3x iPhone 5上出现同样的错误。 5s,6,6s,7都可以正常工作。

这意味着问题在于CoreDataStack或许?

Github repo here

3 个答案:

答案 0 :(得分:1)

几个人问我是否解决了这个问题,所以这就是我所做的。这并不是真正的解决方案,而是更多的解决方法。可能不适合所有人,但对我有用。

我所做的就是删除行

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

CoreDataStack起,问题就消失了……

答案 1 :(得分:0)

我会根据您的代码进行简短的猜测。

我的老派开发人员被配置成员var deviceNumber: Int16 = 1吸引。对齐设置不正确或旧编译器可能导致下一项错误对齐。您可以尝试将其作为结构中的最后一项。

另一个突出的项目是作业sessionInfo.deviceNumber = self.config.deviceNumber。这看起来是将Int16分配给NSNumber,这可能是一个问题。 (我假设SessionInfo是基于整体代码的NSManagedObject,它的初始化器采用上下文参数。这意味着所有数字成员都是NSNumbers。)

尝试将行更改为

sessionInfo.deviceNumber = NSNumber(int:self.config.deviceNumber)

我还不熟悉iOS 10对Core Data的补充,但是从我阅读的内容来看,viewContext是只读的。但是viewContext用于在此代码中创建新对象。当你在调试器中达到这一点时,Xcode控制台是否显示了更多信息?

答案 2 :(得分:0)

NSPersistentContainer是核心数据堆栈的设置,对如何使用它有明确的期望。你在滥用它。 viewContext只读取performForegroundTasksaveViewContextsaveViewContextAndWait。不要更改viewContext的mergePolicy。在performBackgroundTask中只需使用NSPersistentContainer的performBackgroundTask,它具有相同的方法签名。不要使用newBackgroundContext。如果您正在使用NSPersistentContainer,那么您的CoreDataStack应该一无所获。

如果你想要一个不同于NSPersistentContainer设置的不同自定义堆栈,那么不要使用NSPersistentContainer - 只需创建自己的堆栈。但是您尝试编写的设置存在重大问题。当写入同时发生时,从后台上下文和viewContext写入时会出现主要问题。 mergePolicy可以帮助您,但您最终可能会丢失您认为已保存的信息。学习使用NSPersistentContainer设置的堆栈会更好。