You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CoreData loadPersistentStores无回调致App白屏启动失败求助

问题:CoreData新增备份功能后启动无loadPersistentStores回调导致白屏

近几周为CoreData新增了数据库备份功能,上线后收到用户反馈App启动时白屏无法正常运行。经排查,问题根源是loadPersistentStores执行后无回调。此前从未出现该问题,因此怀疑与新增的备份功能相关。

备份功能代码

func backupSync(completionBlock: @escaping ((Bool) -> Void) = { _ in }) {
        do {
            let backupDir = self.createFolder(name: self.backupFolderName, baseUrl: self.getDocumentsDirectory())
            let backupTime = "backup_\(Date().timeIntervalSince1970)"
            
            let backupFile = try CoreDataStack.shared.backup(fileName: backupTime)
            let backupPath = backupDir + "/" + backupFile.lastPathComponent
            
            // copy file
            
            if !FileManager.default.fileExists(atPath: backupPath) {
                try FileManager.default.copyItem(at: backupFile, to: URL(fileURLWithPath: backupPath))
            }
            
            // zip files with password
            
            let zipPath = backupDir + "/\(backupTime).zip"
            SSZipArchive.createZipFile(atPath: zipPath, withFilesAtPaths: [backupPath], withPassword: self.password)
            
            // delete copy file
            
            if FileManager.default.fileExists(atPath: backupPath) {
                try FileManager.default.removeItem(at: URL(fileURLWithPath: backupPath))
            }
            
            "\(zipPath)".debugLog("Local backup")
            
            completionBlock(true)
            
        } catch {
            completionBlock(false)
            print(error)
        }
    }

func backup(fileName: String) throws -> URL {
        let backupfile = try persistentContainer.persistentStoreCoordinator.backupPersistentStore(atIndex: 0, fileName: fileName).fileURL
        return backupfile
    }

func backupPersistentStore(atIndex index: Int, fileName: String) throws -> TemporaryFile {
        precondition(persistentStores.indices.contains(index), "Index \(index) doesn't exist in persistentStores array")
        let sourceStore = persistentStores[index]
        let backupCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)

        let intermediateStoreOptions = (sourceStore.options ?? [:])
            .merging([NSReadOnlyPersistentStoreOption: true],
                     uniquingKeysWith: { $1 })
        let intermediateStore = try backupCoordinator.addPersistentStore(
            ofType: sourceStore.type,
            configurationName: sourceStore.configurationName,
            at: sourceStore.url,
            options: intermediateStoreOptions
        )

        let backupStoreOptions: [AnyHashable: Any] = [
            NSReadOnlyPersistentStoreOption: true,
            NSSQLitePragmasOption: ["journal_mode": "DELETE"],
            // Minimize file size
            NSSQLiteManualVacuumOption: true,
            ]

        let backupFilename = fileName + ".sqlite"
        let backupFile = try TemporaryFile(creatingTempDirectoryForFilename: backupFilename)
        try backupCoordinator.migratePersistentStore(intermediateStore, to: backupFile.fileURL, options: backupStoreOptions, withType: NSSQLiteStoreType)
        return backupFile
}

备份触发时机

场景1:sceneDidEnterBackground中通过Operation结合UIApplication.shared.beginBackgroundTask执行

class BackUpOpeartion: Operation {
   
  override func main() {
    LocalBackupManager.shared.backupSync()
  }
   
  static func canHandler() -> Bool {
    let backupStrategy = LocalUserDefaults.standard.backupUiModel
    let lastBackUpDate = LocalUserDefaults.standard.lastBackUpDate
     
    let now = Date()
    let sixHour: TimeInterval
     
    #if DEBUG
    sixHour = TimeInterval(0.15 * 60 * 60)
    #else
    sixHour = TimeInterval(backupStrategy.backupFrequency) // 6h
    #endif
     
    let backupDate = lastBackUpDate + sixHour
     
    // Clean the database at most once per week.
    guard now > backupDate else {
      "It's not time to backup".debugLog()
      return false
    }
     
    return true
  }
   
  static func doWhenEnterBackground() {
    if !canHandler() {
      return
    }
     
    var bgtToken = UIBackgroundTaskIdentifier.invalid
    bgtToken = UIApplication.shared.beginBackgroundTask(expirationHandler: {
      UIApplication.shared.endBackgroundTask(bgtToken)
    })
     
    guard bgtToken != .invalid else{
      return
    }
     
    DispatchQueue.global(qos: .utility).async {
      LocalBackupManager.shared.backupSync { succeed in
        DispatchQueue.main.async {
          if succeed {
            LocalUserDefaults.standard.lastBackUpDate = Date()
          }
          UIApplication.shared.endBackgroundTask(bgtToken)
        }
      }
    }

  }
   
}

场景2:AppDelegate中的BGProcessingTask

func registerBackgroundTasks() {
    BGTaskScheduler.shared.register(forTaskWithIdentifier: dbBackupId, using: nil) { task in
      self.backupDb(task: task as! BGProcessingTask)
    }
  }

func scheduleDbBackup() {
    if !BackUpOpeartion.canHandler() {
      return
    }
     
    let request = BGProcessingTaskRequest(identifier: dbBackupId)
    request.requiresNetworkConnectivity = false
    request.requiresExternalPower = false
     
    do {
      try BGTaskScheduler.shared.submit(request)
      "submit DbBackup".debugLog()
    } catch {
      print("Could not schedule app refresh: \(error)")
    }
  }

func backupDb(task: BGProcessingTask) {
     
    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 1
     
    let operation = BackUpOpeartion()
     
    task.expirationHandler = {
      queue.cancelAllOperations()
    }
     
    operation.completionBlock = {
      let success = !operation.isCancelled
      if success {
        LocalUserDefaults.standard.lastBackUpDate = Date()
      }
      task.setTaskCompleted(success: success)
    }
  }

不清楚在何种情况下会导致CoreDataStack的loadPersistentStores无回调,恳请各位帮忙分析原因,谢谢!

内容的提问来源于stack exchange,提问作者wanbo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 17:01:21