Могу ли я отменить JSONEncoder Swift?

#swift #jsonencoder

Вопрос:

У меня есть JSONEncoder, кодирующий файл размером 20 МБ, для обработки которого требуется целая вечность. Если данные, которые он обрабатывает, изменятся, я хотел бы отменить кодировку и перезапустить процесс кодирования, но я не могу придумать, как это сделать. Есть какие-нибудь идеи? Я мог бы снова вызвать JSONEncoder.encode, но теперь у меня было бы два 30-секундных запущенных процесса и удвоенный объем памяти и накладных расходов процессора. Было бы замечательно иметь возможность отменить предыдущее.

ПРАВКА: Некоторые из вас просили показать мой кодировщик. Вот тот, который, я бы сказал, вызывает самое большое узкое место…

 func encode(to encoder: Encoder) throws {
        try autoreleasepool {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(brush, forKey: .brush)

            if encoder.coderType == CoderType.export {
                let bezierPath = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIBezierPath.self, from: beziersData)
                let jsonData = try UIBezierPathSerialization.data(with: bezierPath, options: UIBezierPathWritingOptions.ignoreDrawingProperties)
                let bezier = try? JSONDecoder().decode(DBBezier.self, from: jsonData)
                try container.encodeIfPresent(bezier, forKey: .beziersData)
            } else {
                try container.encodeIfPresent(beziersData, forKey: .beziersData)
            }
        }
    }
 

Комментарии:

1. Инкапсулируйте свой кодер в отменяемый объект, например, NSOperation или, может быть, с помощью объединения?

2. Какую версию Swift вы используете?

3. В случае, если данные вашей модели позволяют вам это, вам следует рассмотреть возможность выполнения этого по частям/подзадачам, скажем, 20 МБ разделено на 100 блоков, вы выполняете этот процесс в цикле for по одному, перед запуском каждой подзадачи вы можете проверить, нужно ли вам продолжать или нет (этот процесс кодирования отменен?). Если он был отменен, вы можете вернуться с этого момента без кодирования всех данных и очистки ваших незавершенных файлов и т.д. Пока вы делаете это одним выстрелом, оно не может быть отменено после того, как оно было инициировано.

4. @SalmanKhakwani Спасибо за ваш ответ. Я использую Swift 5

5. Мне также интересно, почему файл размером 20 МБ вызывает проблемы с производительностью. JSONEncoder-это не тот быстрый код, который вы можете получить, но все же не настолько медленный, чтобы 20 МБ было проблемой. Вы можете рассмотреть более быструю альтернативу, которая создает пользовательское представление из 20 МБ JSON за 1/10 часть времени — или даже быстрее.

Ответ №1:

Вы можете использовать OperationQueue и добавить свою длительную задачу в эту очередь операций.

 var queue: OperationQueue?
//Initialisation
if queue == nil {
    queue = OperationQueue()
    queue?.maxConcurrentOperationCount = 1
}
queue?.addOperation {
    //Need to check the isCanceled property of the operation for stopping the ongoing execution in any case.
    self.encodeHugeJSON()
}
 

Вы также можете отменить задачу, когда захотите, используя следующий код:

 //Whenever you want to cancel the task, you can do it like this
queue?.cancelAllOperations()
queue = nil
 

Что такое очередь операций:

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

Ссылочные ссылки:

Комментарии:

1. В документах OperationQueue. cancelAllOperations говорится Canceling the operations does not automatically remove them from the queue or stop those that are currently executing. , что это отменяет все ожидающие задачи в очереди, задачи в полете не могут быть отменены.

2. @TarunTyagi 1 Спасибо за эту информацию. Я думаю, что оперативная группа может использовать флаг isCanceled в очереди операций и отказаться от своей текущей задачи, когда обнаружит, что флаг включен.

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

4. @TarunTyagi Именно так, как я думал, вела себя Очередь операций. Так что, похоже, нет никакого способа отменить его, как только он «в полете». Разбить его на более мелкие куски кажется единственным вариантом в настоящее время, которого я надеялся избежать.

5. @user139816 Breaking it into smaller chunks seems like the only option -самый разумный способ сделать это. Это позволит вам отменить процесс кодирования в середине полета (процесс запущен (шаги = 100), он находится на шаге 15, пользователь отменен, вы отмечаете, что этот процесс был отменен, он все равно завершит шаг 15, перед началом шага 16 он проверит отмену и выйдет оттуда).