#swift #for-loop #asynchronous #dispatch-queue
#swift #для цикла #асинхронный #отправка-очередь
Вопрос:
В моем приложении есть цикл, обработка которого может занять несколько секунд.
Я хочу, чтобы экран немедленно отключался, пока цикл выполняется в фоновом режиме.
Возможно ли это?
Я попытался добавить только цикл:
DispatchQueue.main.async {
for _ in 1...self.numberOfTransactionsToAdd {
let newTransaction = Transaction()
var timeAdded = 1.months
newTransaction.transactionAmount = self.amountTextField.text!.toDouble()
newTransaction.transactionDescription = self.descriptionTextField.text
newTransaction.transactionDate = self.datePicked
newTransaction.transactionCategory = self.categoryPicked
newTransaction.repeatInterval = self.repeatInterval
newTransaction.transactionSubCategory = self.subcategoryPicked
newTransaction.subCategoryName = self.subcategoryPicked!.subCategoryName
try! self.realm.write {
self.realm.add(newTransaction)
}
self.datePicked = self.datePicked timeAdded
}
}
}
Я попытался добавить всю функцию целиком:
@IBAction func doneButtonPressed(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
DispatchQueue.main.async {
self.saveTransaction()
}
}
Но экран все еще зависает до того, как просмотр будет отклонен.
Я впервые использую DispatchQueue. Я прочитал документацию Apple по этому вопросу, но она крайне расплывчата.
Это вообще правильный способ сделать это?
Может кто-нибудь помочь новичку правильно это сделать?
Комментарии:
1. Почему их два
DispatchQueues
? Показан ли первый пункт кодаsaveTransaction
?2. Эти фрагменты кода были просто двумя разными вещами, которые я пробовал, а не вместе.
Ответ №1:
Проблема заключается в асинхронном вызове самой основной очереди для работы, не связанной с пользовательским интерфейсом (фоновая работа). Заменить DispatchQueue.main.async
на DispatchQueue.global().async
. Причина в том, что все задачи, не связанные с пользовательским интерфейсом, должны выполняться асинхронно из основной очереди.
После выполнения вышеуказанного исправления у вас все равно будет другая проблема, которая будет связана с доступом к элементам пользовательского интерфейса из неосновной очереди (поскольку теперь наш код будет выполняться в глобальной очереди). Причина в том, что вы будете извлекать данные элементов пользовательского интерфейса (например, текст amountTextField и т. Д.) Из глобальной очереди. Храните данные пользовательского интерфейса в локальной переменной и используйте локальные переменные в коде цикла вместо вызова amountTextField и других текстовых полей.
var amountData: Double
var description: String
@IBAction func doneButtonPressed(_ sender: UIButton) {
amountData = amountTextField.text!.toDouble()
descriptionTextField = descriptionTextField.text
DispatchQueue.global().async {
self.saveTransaction()
}
self.dismiss(animated: true, completion: nil)
}
func saveTransaction() {
for _ in 1...self.numberOfTransactionsToAdd {
.
.
newTransaction.transactionAmount = self.amountData
newTransaction.transactionDescription = self.descriptionTextField
.
.
.
}
}
Ответ №2:
Похоже, ваша проблема связана с GCD:
@IBAction func doneButtonPressed(_ sender: UIButton) {
DispatchQueue.global(qos: .background).async {
// do here your loading, fetching the data etc
self.saveTransaction()
DispatchQueue.main.async {
// do here ui works, because user doesn't want to wait for touching etc so it has to be asp
self.dismiss(animated: true, completion: nil)
}
}
}
Комментарии:
1. Когда я пытаюсь это сделать, я получаю сообщение «Завершение работы приложения из-за неперехваченного исключения»RLMException», причина: «Доступ к области из неправильного потока».
2. Я не имел в виду неуважения, я действительно ценю помощь. Я рассмотрю проблему и буду обновлять эту страницу.