Search code examples
iosswiftgrand-central-dispatchsemaphorephasset

Why app is blocked by semaphore?


I have the following function that suppose to return [CIImage] for my purpose - displaying some metadata of photos in tableView.

func getCIImages() -> [CIImage] {
    var images = [CIImage]()
    let assets = PHAsset.fetchAssetsWithMediaType(.Image, options: nil)

    for i in 0..<assets.count {
        guard let asset = assets[i] as? PHAsset else {fatalError("Cannot cast as PHAsset")}  
        let semaphore = dispatch_semaphore_create(0)

        asset.requestContentEditingInputWithOptions(nil) { contentEditingInput, _ in
            //Get full image
            guard let url = contentEditingInput?.fullSizeImageURL else {return}
            guard let inputImage = CIImage(contentsOfURL: url) else {return}
            images.append(inputImage)
            dispatch_semaphore_signal(semaphore)
        }
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
    }
    return images
}

but it stucks in semaphore wait and didn't go further. I have walked through many tutorials but other variants of GCD don't works. I think it's because of simulator, I don't know, can't test on real device. Please help.


Solution

  • guards inside requestContentEditingInputWithOptions callback closure prevents signal sent to semaphore. In such cases (when you need cleanup actions) it is good to use defer. In your case:

    asset.requestContentEditingInputWithOptions(nil) { contentEditingInput, _ in
        defer { dispatch_semaphore_signal(semaphore) }
    
        //Get full image
        guard let url = contentEditingInput?.fullSizeImageURL else {return}
        guard let inputImage = CIImage(contentsOfURL: url) else {return}
        images.append(inputImage)
    }
    

    UPDATE

    Apart from cleanup bug there is another one. Completion closure of requestContentEditingInputWithOptions called on main thread. Which means that if you blocking main thread with semaphore: completion closure is blocked form executing as well. To fix blocked semaphore issue you need call getCIImages on a different thread than main.

    Anyway making asynchronous things synchronous is wrong. You should think of different approach.