2017-01-07 11 views
3

Я проверил мой код с 3 х iPhone 5-х, через 5 сек, 6, 6с и 7.EXC_BAD_ACCESS код = EXC_ARM_DA_ALIGN

я получаю вышеуказанную ошибку на все 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) 
      } 
    } 
} 

переменная конфигурации простой структура:

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

Edit:

Я создал усеченную тестовое приложение, состоящее из только CoreDataStack и модель с одного объекта с одним атрибутом типа строки. Я по-прежнему получаю ту же ошибку на 3x iPhone 5 только. 5s, 6, 6s, 7 все работают нормально.

Возможно, это проблема с CoreDataStack?

Github репо here

+0

В проекте github отсутствует файл CoreDataStack.swift. Копирование из результатов поиска «log» недоступно. После комментирования «log out» и «running» я получаю «успех» на всех симуляторах, включая iPhone 5. Получаете ли вы ошибку на симуляторе или только на самом устройстве? – oyalhi

+0

Вы нашли решение или обходной путь для решения этой проблемы? У меня более или менее та же проблема и я не могу понять. –

ответ

0

Я возьму пару догадок, основанный на короткий взгляд на свой код.

Разработчик старой школы во мне нарисован на член конфигурации var deviceNumber: Int16 = 1. Неправильные настройки выравнивания или старые компиляторы могут привести к неправильному выравниванию следующего элемента. Вы можете попробовать сделать его последним элементом в структуре.

Другой элемент, который выделяется, является присвоением sessionInfo.deviceNumber = self.config.deviceNumber. Это похоже на назначение Int16 NSNumber, что может быть проблемой. (Я предполагаю, что SessionInfo является NSManagedObject на основе общего кода и его инициализатор принимает контекст аргумента. Это означало бы, все числовые члены NSNumbers.)

Попробуйте изменить строку

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

Я m еще не знакомы с дополнениями iOS 10 к Core Data, но из того, что я читаю, viewContext доступен только для чтения. Но viewContext используется для создания нового объекта в этом коде. Консоль Xcode показывает больше информации, когда вы добираетесь до этой точки в отладчике?

+0

Спасибо за предложения. Я попробовал это и убрал все, что мог придумать, чтобы решить проблему, но ничего не получилось. Я создал новый проект с минимальным кодом, необходимым для дублирования проблемы. См. Отредактированный вопрос для ссылки репо. Был бы признателен, если бы вы могли посмотреть! – doovers

+0

Вы попробовали обе части? Как насчет части NSNumber? (Я отредактировал свой ответ, чтобы включить образец строки.) –

+0

Да, я тоже пробовал с NSNumber. Но в образце проекта я удалил все числа и только один строковый атрибут в сущности, и это все еще происходит. – doovers

0

NSPersistentContainer - это установка для базового стека данных с явным ожиданием того, как его использовать. Вы злоупотребляете им. viewContext только для чтения удалить performForegroundTask, saveViewContext и saveViewContextAndWait. Не меняйте значение mergePolicy viewContext. В performBackgroundTask просто используйте команду NSPersistentContainer, выполнив таковую подпись метода. Не используйте newBackgroundContext. Если вы используете NSPersistentContainer, ваш CoreDataStack должен делать практически ничего.

Если вы хотите другой пользовательский стек в отличие от того, что устанавливает NSPersistentContainer, то не используйте NSPersistentContainer - просто создайте свой собственный стек. Но настройки, которые вы пытаетесь написать, имеют серьезные проблемы. Запись из фоновых контекстов и viewContext имеет основные проблемы, когда записи происходят одновременно. mergePolicy может помочь вам, но вы можете потерять информацию, которую, как вы считали, вы сохранили. Вам намного лучше научиться использовать стек, который устанавливает NSPersistentContainer.

+0

Спасибо за предложения. То, что вы говорите, имеет смысл, но я не понимаю, почему я не могу использовать 'newBackgroundContext'. Причина, по которой я делаю это, - это то, что я хочу, чтобы общий контекст создавал несколько объектов и сохранял их в партиях для ограничения ввода-вывода. Это противопоказано? Кроме того, можете ли вы указать мне хорошие ссылки для соответствующего использования «NSPersistentContainer», если у вас есть хорошие ресурсы. – doovers

+0

Сохранение в партиях - хорошая идея. Я бы предложил использовать 'performBackgroundTask' для этого. Он имеет много преимуществ перед 'newBackgroundContext'. Сначала это гарантирует, что ваш код будет выполнен в правильной нити. Во-вторых, убедитесь, что у вас нет двух записей, которые происходят одновременно. В-третьих, явное ожидание того, что этот контекст одноразовый - вы используете их в своем блоке, а затем не используете их снова. Я обнаружил, что это лучшая система, в которой есть фоновой контекст. –

+0

Я немного смущен. Если я создаю сущности, использующие несколько вызовов для 'performBackgroundTask', как я могу сохранить пакеты? Я не могу создать все сущности в одном блоке, поэтому я рассуждаю об использовании 'newBackgroundContext' для создания общего контекста для каждого нового объекта. Что мне здесь не хватает? – doovers

Смежные вопросы