OperationQueue - crash when editing the same array from multiple operations

215 views Asked by At

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()

Edit 2: The crash is always the same: enter image description here

enter image description here

0

There are 0 answers