2015-10-29 4 views
0

Я нашел так много решений для обновления строки выполнения в пределах одного потока и контроллера представления, однако они, казалось, были не похожими случаями, как мои.Проблема с параллелизмом многопроцессорных рядов за пределами главного контроллера представления

В моем приложении контроллер основного вида вызывает loadIntoCoreData() (реализован в классе MyLoadingService), который асинхронно загружает данные в данные ядра другим потоком. Эта функция должна постоянно обновлять процент загрузки (который записывается в NSUserDefaults.standardUserDefaults()) в основной поток, чтобы он мог отображаться на панели выполнения в главном контроллере. Я никогда не использовал время цикла в MainViewController непрерывно получать текущее процентное значение, как показано ниже:

class MainViewController { 

    override func viewDidLoad() { 
     MyLoadingService.loadIntoCoreData() { result in 

      NSUserDefaults.standardUserDefaults().setBool(false, forKey: "isLoading") 
      // do something to update the view 

     } 
     self.performSelectorInBackground("updateLoadingProgress", withObject: nil) 
    } 

    func updatingLoadingProgress() { 
     let prefs = NSUserDefaults.standardUserDefaults() 
     prefs.setBool(true, forKey: "isLoading") 

     // here I use a while loop to listen to the progress value 
     while(prefs.boolForKey("isLoading")) { 

      // update progress bar on main thread 
      self.performSelectorOnMainThread("showLoadingProcess", withObject: nil, waitUntilDone: true) 
     } 
     prefs.setValue(Float(0), forKey: "loadingProcess") 
    }  

    func showLoadingProcess() { 
     let prefs = NSUserDefaults.standardUserDefaults() 
     if let percentage = prefs.valueForKey("loadingProcess") { 
      self.progressView.setProgress(percentage.floatValue, animated: true) 
     } 
    } 
} 

А в классе функции loadIntoCoreData:

class MyLoadingService { 

    let context = (UIApplication.sharedApplication()delegate as! AppDelegate).managedObjectContext! 

    func loadIntoCoreData(source: [MyModel]) { 
     var counter = 0 
     for s in source { 
      //load into core data using the class context 

      NSOperationQueue.mainQueue.addOperationWithBlock({ 
       // updating the value of "loadingProcess" in NSUserDefaults.standardUserDefaults() 
       // and synchronize it on main queue 
      }) 
      counter++ 
     } 
    } 
} 

Приведенный выше код может успешно запустить прогресс бар, однако он часто сталкивается с BAD_ACCESS или некоторыми другими исключениями (например, «Не удается обновить объект, который никогда не был вставлен») из-за конфликтов в контексте основных данных (кажется, что managedObjectContext не затрагивается основным потоком). Поэтому вместо того, чтобы использовать прослушивание во время цикла в основном потоке, я рассматриваю использование NSOperationQueue.performSelectorOnMainThread для подтверждения основного потока после каждой записи. Поэтому я поставил свой контроллер представления в качестве аргумента sender в loadCoreData и вызвал performSelectorOnMainThread("updateProgressBar", withObject: sender, waitUntilDone: true), но не смог с ошибкой «нераспознанный селектор, отправленный в класс« XXXXXXXX ». Поэтому я хотел бы спросить, возможно ли обновлять объект UI между потоками? Или, как изменить мое предыдущее решение, чтобы можно было разрешить конфликты контекста основных данных? Любые решения оцениваются.

class MyLoadingService { 
    func loadIntoCoreData(sender: MainViewController, source: [MyModel]) { 
     var counter = 0 
     for s in source { 
      //load into core data using the class context 

      NSOperationQueue.mainQueue.addOperationWithBlock({ 
       // updating the value of "loadingProcess" in NSUserDefaults.standardUserDefaults() 
       // and synchronize it on main queue 
      }) 
      NSOperationQueue.performSelectorOnMainThread("updateProgressBar", withObject: sender, waitUntilDone: true) 
      counter++ 
     } 
    } 
    func updateProgressBar(sender: MainViewController) { 
     sender.progressView.setProgress(percentage, animated: true) 
    } 
} 

class MainViewController { 
    override func viewDidLoad() { 
     MyLoadingService.loadIntoCoreData(self) { result in 

      // do something to update the view 
     } 
    } 
} 

ответ

0

Во-первых, вы злоупотребляете NSUserDefaults ужасными способами. Документация описывает его как это ...

Класс NSUserDefaults предоставляет программный интерфейс для взаимодействующий с системой по умолчанию. Система по умолчанию позволяет приложению настроить его поведение в соответствии с предпочтениями пользователя. Например, вы можете разрешить пользователям определять, какие единицы измерения отображают ваше приложение или как часто документы автоматически сохраняются. Приложения записывают такие предпочтения, присваивая значения набору параметров в базе данных по умолчанию пользователя. Параметры называются значениями по умолчанию, поскольку они обычно используются для определяют состояние приложения по умолчанию при запуске или способ, которым он действует по умолчанию.

Вы используете его для хранения глобальной переменной.

Кроме того, вы полностью злоупотребляете процессором пользователя в своем цикле, где вы постоянно проверяете значение в настройках по умолчанию пользователя и отсекаете селектор в основной поток. «Злоупотребление CPU» даже не приближается к описанию того, что делает этот код.

Вы должны использовать NSProgress для получения информации о ходе выполнения. Существует WWDC 2015 presentation, посвященный исключительно использованию NSProgress.


К основному использованию данных.

К сожалению, поскольку вы намеренно отредактировали весь основной код данных, невозможно сказать, что происходит не так.

Однако, исходя из того, что я вижу, вы, вероятно, пытаетесь использовать этот контекст управляемого объекта из своего делегата приложения (который, вероятно, все еще создается с использованием устаревшей политики ограничения) из фонового потока, который является кардинальным грехом самый высокий порядок в отношении основных данных.

Если вы хотите импортировать данные в качестве долгосрочной операции, используйте частный контекст и выполните операции в фоновом режиме. Используйте NSProgress, чтобы сообщить прогресс любому, кто хочет слушать.


EDIT

Спасибо за совет по моему основному использованию контекста данных. Я выкопал все контексты в моем коде и повторно организовал контексты внутри, проблема конфликта больше не происходит. Что касается NSProgress, то жаль, что презентация WWDC фокусируется на функции на iOS 9 (тогда как мое приложение должно быть компактным на устройствах iOS 8). Тем не менее, хотя я использую NSProgress, я должен все еще рассказать основному потоку, сколько данных уже имеет данные ядра (на другой поток), правильно? Как поток на NSProgress знает о ходе загрузки моего основного потока данных? - whitney13625

Вы все еще можете использовать NSProgress для iOS8, то единственное реальное различие в том, что вы не можете явно добавлять детей, но неявным образом все еще работает, и это видео объясняет, как хорошо.

Вы действительно должны смотреть все видео и забыть о части iOS9, за исключением того, что знаете, что вы должны добавлять дочерние объекты неявно, а не явно.

Кроме того, сообщение this pre-iOS9 blog должно прояснить любые возникшие у вас вопросы.

+0

Спасибо за совет по использованию моего основного использования данных. Я ворвался во все контексты моего кода и повторно организовал контексты внутри, проблема конфликта больше не происходит. Что касается 'NSProgress', то очень жаль, что презентация WWDC фокусируется на функции на iOS 9 (в то время как мое приложение должно компактно работать на устройствах iOS 8). Однако, хотя я использую 'NSProgress', я все равно должен сказать основному потоку, сколько данных уже имеют данные ядра (в другом потоке), правильно? Как поток на «NSProgress» знает ход загрузки в моем основном потоке данных? – whitney13625

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

  • Нет связанных вопросов^_^