iOS Concurrency with GCD and Operations - Part 7: | Ray Wenderlich

In this video tutorial, you'll learn how to use an Operation to wrap an asynchronous function such as a network call.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/3648-ios-concurrency-with-gcd-and-operations/lessons/7
1 Like

Why not keep the Swift 3 convention with lowercase letters for the enum, and then use the capitalised property on rawValue within the keyPath computed variable?

I didn’t know about capitalalized until recently :blush:

:slight_smile: Great tutorial by the way. The way a tutorial should be…

1 Like

But Why I need to make async NSOperation while NSOperationQueue is executing the operation asynchronously…

Can I do Some UI Stuffs asynchronously by using NSOperations …

hi Rakesh: if you don’t override what it means for the operation to be finished, the system will think it’s finished right away, but it’s really still executing, or maybe waiting its turn.

this matters if you’re making another operation dependent on it — you want the async operation to really finish before starting the dependent operation.

standalone operations run synchronously by default; operations in an operation queue run off the main queue.

It means If I run a nsoperation and It is using some resources then It will not be available for the other nsoperation to used while Ist operation is executing ???

the operation doesn’t lock the resource; if both operations are just reading, there’s no problem

Part 11 shows you how to make a class thread safe: this involves a private queue in the class itself

In Demo I read this “For synchronous tasks, you can create an Operation subclass by overriding the main() method.” So If I am calling webservices using NSUrlSession in Operation Queue by Overriding main function.So Is It wrong ??

URLSession tasks are asynchronous. If you want to use them in an OperationQueue, you should subclass AsyncOperation and override main as described in this video.

Thanks Audrey Your videos are quite awesome.

Hi Audrey,

In video 7, when you set the state property, why should we call the didChangeValueForKey twice in willSet and didSet methods ? Is it not enough if we just call it once in each function ?

hi Preetham: it’s actually willChange in willSet and didChange in didSet, but it’s one of those Apple Mysteries that I just take on faith. Apple’s KVO documentation says to write both for manual notification. In this case, we’re notifying the Operations scheduler, which might pay attention to one set or the other, or to both, for different purposes.

Yes, sorry for the typo about willSet and didSet.
thanks Audrey. Loving the course :slight_smile: Please come up with other topics in the future

URLSession update coming soon! :grinning:

Also, when AsyncOperation state is about to change from .executing to .finished, the state-will-change keyPath is Operation’s isExecuting.

After the change, the state-did-change keyPath is Operation’s isFinished.

I noticed tsan complaining about setting the state as a race condition.

I think the solution is to not set the state after main() but to actually override main(), check if it’s cancelled, then set the state. Then in the subclass, go ahead and call super.main() and go about your normal async call.

override open func start() {
	if isCancelled {
		state = .finished
		return
	}
	main()
}

override open func main() {
	if self.isCancelled { return }
	self.state = .executing
}

Then

class DownloadOperation: AsyncOperation {
    override func main() {
		super.main()
	    ...
    }
}

Nope. Still seeing race conditions…
Seems to be centered around cancel. These are for UITableView cells bg images, so it’s as prefetch cancels them. I also cancel them when doing a beginUpdates as the IndexPath changes. Although I think I will modify the downloadsInProgress to be indexed by the ObjectID instead of the cell index.

So, I think the issue was an oversight on my part. I was not removing the Operation from the downloadsInProgress list using the completionBlock of the Operation. I think that was causing the issues.

I’m glad you found a fix!

So I think the issue might be the return thread of the URLSession:dataTask is not the same thread as the Operation.
So what I did was grab the current OperationQueue, then grab it’s underlyingQueue and then async back to it to set the Operation data.

override func main() {
	super.main()
	let queue = OperationQueue.current
	if let url = URL(string: path) {
		let request = NSMutableURLRequest(url: url)
		request.httpMethod = "GET"
		let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
			DataManager.shared.removeActivity()
			if let thread = queue?.underlyingQueue {
				thread.async {
					print("^ I'm back on the current queue!")
					self.data = data
					self.response = response
					self.error = error
					self.state = .finished
				}
			} else {
				print("^ I couldn't figure out the current queue")
				self.data = data
				self.response = response
				self.error = error
				self.state = .finished
			}
		}
		if self.isCancelled { return }
		DataManager.shared.addActivity()
		task.resume()
	} else {
		self.state = .finished
	}
}