Leave a rating/review
Challenge: TiltShiftOperations in OperationQueue
The playground in the starter folder has an array of input images, an empty filteredImage array to hold the tilt-shifted output images and the TiltShiftOperation subclass from video 15. Use this operation in an OperationQueue to filter all the input images.
There’s also a serial operation queue: Use this to control access by multiple operations appending to the array of output images.
Hint: Define a completion block for the filter operation before adding it to the operation queue.
Pause this video while you complete this challenge, then resume playing the video to see my solution.
First, a word about this serial operation queue:
let appendQueue = OperationQueue()
appendQueue.maxConcurrentOperationCount = 1
You’ve already used a custom serial dispatch queue to control access to a shared resource. This is how you do the same thing with operations. A serial dispatch queue would be more efficient, but this is an opportunity to show you how to create a serial OperationQueue.
The reason you need one here is because the filteredImages array is a resource shared among all the filter operations you’re going to create soon. There will be multiple filter operations appending to this array at the same time. Swift arrays are value types, and are copied on write, so you shouldn’t have to worry about concurrent changes. But I’ve found conflicting reports in forums, especially when an array is a property of a class object, so it’s better to show you a way to stay safe!
OK, here’s the TiltShiftOperation you created in the Operations video. Now, create an OperationQueue for the filter operations:
let filterQueue = OperationQueue()
Loop over the images array, creating a TiltShiftOperation for each image, then adding the operation to filterQueue:
let filterOp = TiltShiftOperation(image: image)
filterQueue.addOperation(filterOp)
When the tiltShift function creates the outputImage, you want to append it to the filteredImage array. The place to do that is in the operation’s completionBlock property.
filterOp.completionBlock = {
}
completionBlock takes no arguments and returns nothing. First, check that there actually is an outputImage:
guard let output = filterOp.outputImage else { return }
If there’s an outputImage, go ahead and append it to the filteredImages array:
appendQueue.addOperation {
filteredImages.append(output)
}
You use the serial operation queue, so only one operation at a time can append.
It’s good practice to define an operation’s completionBlock before you add it to an operation queue, so select all these lines and press Option-Command-[ to move them up, above the addOperation line.
Here in a playground, it’s safe to call the operation queue’s waitUntilAllOperationsAreFinished method:
filterQueue.waitUntilAllOperationsAreFinished()
So the playground will wait until all the image transforms finish. Then ask to see the filteredImages. Note that filteredImages is currently empty.
filteredImages
// In case the playground won't display the array of images:
filteredImages[0]
filteredImages[1]
filteredImages[2]
filteredImages[3]
filteredImages[4]
There might be a playground bug that prevents showing the array of images, so ask for each image individually. Now run the playground:
[w 1,024 h 768, w 1,024 h 1,176, w 1,024 h 768, w 1,024 h 768, w 1,024 h 768]
And here are the filtered images. Tap the quick look eye next to each image, to see it’s been tilt-shifted! Next, you’ll create an AsyncOperation subclass of Operation, so you can add asynchronous tasks to an operation queue.