I have an OperationQueue
with multiple custom Operations
which all append to the same array on completion (each operation downloads a file from user's iCloud and when it's done it appends the file to the array)
This, sometimes, causes the app to crash, because several operations
try to edit the array at the same time.
How can I prevent this and only edit the array 1 operation at a time but running all operations simultaneously?
I must use OperationQueue
because I need the operations to be cancelable.
func convertAssetsToMedias(assets: [PHAsset],
completion: @escaping (_ medias: [Media]) ->()) {
operationQueue = OperationQueue()
var medias: [Media] = []
operationQueue?.progress.totalUnitCount = Int64(assets.count)
for asset in assets {
// For each asset we start a new operation
let convertionOperation = ConvertPHAssetToMediaOperation(asset)
convertionOperation.qualityOfService = .userInteractive
convertionOperation.completionBlock = { [unowned convertionOperation] in
let media = convertionOperation.media
medias.append(media) // CRASH HERE (sometimes)
self.operationQueue?.progress.completedUnitCount += 1
if let progress = self.operationQueue?.progress.fractionCompleted {
self.delegate?.onICloudProgressUpdate(progress: progress)
}
convertionOperation.completionBlock = nil
}
operationQueue?.addOperation(convertionOperation)
}
operationQueue?.addBarrierBlock {
completion(medias)
}
}
Edit 1:
The Media
file itself is nothing big, just a bunch of metadata and a url to an actual file at documents directory. There are usually about 24 medias max at 1 run. The memory is barely increasing during those operations. The crash never occured due to a lack of memory.
The operation ConvertPHAssetToMediaOperation
is a subclass of AsyncOperation
where isAsynchronous
propery is set to true
.
That's how I construct the Media
object in the end of each operation:
self.media = Media(type: mediaType, url: resultURL, creationDate: date)
self.finish()