Comme d'autres l'ont souligné, vous voulez éviter d'attendre sur le thread principal, en risquant le blocage. Ainsi, alors que vous pouvez le repousser dans une file d'attente globale, l'autre approche consiste à utiliser l'un des nombreux mécanismes permettant d'effectuer une série de tâches asynchrones. Les options incluent une sous-classe asynchrone Operation
ou des promesses (par exemple, PromiseKit).
Par exemple, pour envelopper la tâche d'enregistrement des images dans une asynchrone Operation
et les ajouter à un OperationQueue
vous pouvez définir votre image opération de sauvegarde comme ceci:
class ImageSaveOperation: AsynchronousOperation {
let image: UIImage
let imageCompletionBlock: ((NSError?) -> Void)?
init(image: UIImage, imageCompletionBlock: ((NSError?) -> Void)? = nil) {
self.image = image
self.imageCompletionBlock = imageCompletionBlock
super.init()
}
override func main() {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
imageCompletionBlock?(error)
complete()
}
}
Ensuite, en supposant que vous aviez un tableau, images
, ce qui était un [UIImage]
, vous pouvez alors faire:
let queue = OperationQueue()
queue.name = Bundle.main.bundleIdentifier! + ".imagesave"
queue.maxConcurrentOperationCount = 1
let operations = images.map {
return ImageSaveOperation(image: $0) { error in
if let error = error {
print(error.localizedDescription)
queue.cancelAllOperations()
}
}
}
let completion = BlockOperation {
print("all done")
}
operations.forEach { completion.addDependency($0) }
queue.addOperations(operations, waitUntilFinished: false)
OperationQueue.main.addOperation(completion)
vous pouvez évidemment personnaliser cette option pour ajouter une logique de nouvelle tentative en cas d'erreur, mais qui est probablement pas nécessaire maintenant parce que la racine du « à o occupé "problème était le résultat de trop de demandes d'enregistrement simultanées, que nous avons éliminé. Cela ne laisse que des erreurs qui sont peu susceptibles d'être résolues en réessayant, donc je n'ajouterais probablement pas de logique de réessai. (Les erreurs sont plus susceptibles d'être des échecs d'autorisations, de l'espace, etc.) Mais vous pouvez ajouter une nouvelle tentative si vous le voulez vraiment. Plus probablement, si vous avez une erreur, vous pourriez vouloir annuler toutes les opérations restantes dans la file d'attente, comme je l'ai ci-dessus.
Note, les sous-classes ci-dessus AsynchronousOperation
, qui est juste une sous-classe Operation
pour laquelle isAsynchronous
retours true
.Par exemple:
/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `completeOperation()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `completeOperation()` is called.
public class AsynchronousOperation : Operation {
private let syncQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".opsync")
override public var isAsynchronous: Bool { return true }
private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
return syncQueue.sync { _executing }
}
set {
willChangeValue(forKey: "isExecuting")
syncQueue.sync { _executing = newValue }
didChangeValue(forKey: "isExecuting")
}
}
private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
return syncQueue.sync { _finished }
}
set {
willChangeValue(forKey: "isFinished")
syncQueue.sync { _finished = newValue }
didChangeValue(forKey: "isFinished")
}
}
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func complete() {
if isExecuting { isExecuting = false }
if !isFinished { isFinished = true }
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
}
Maintenant, je comprends les files d'attente d'opération (ou promesses) va sembler exagéré pour votre situation, mais il est un modèle utile que vous pouvez utiliser chaque fois que vous avez une série de tâches asynchrones. Pour plus d'informations sur les files d'attente d'opération, n'hésitez pas à vous reporter au Concurrency Programming Guide: Operation Queues.
Vous devriez essayer de mettre votre code dans la file d'attente Dispatach.global cela aidera sûrement –
@MikeAlter Cela a aidé. Cela fonctionne maintenant mais je ne sais pas pourquoi, pouvez-vous m'aider à expliquer? Merci – user339946