Bummer! This is just a preview. You need to be signed in with a Pro account to view the entire video.
Start a free Basic trial
to watch this video
Operation and OperationQueue are a set of APIs built on top of Grand Central Dispatch. They are higher level APIs offering a bit more control and flexibility than GCDs lightweight APIs.
-
0:00
Operation and
-
0:01
operation cues are a set of APIs built on top of Grand Central Dispatch.
-
0:07
Their high level APIs offering a bit more control and
-
0:10
flexibility than GCD's lightweight APIs.
-
0:14
They offer features like dependencies,
-
0:16
where you can make one operation depend on the other.
-
0:19
For example, we want to download a photo and once it's downloaded, resize it.
-
0:25
There are other features like canceling operations, querying their state, and
-
0:29
finally, seeing how many operations are there in a queue.
-
0:33
In addition, your code can easily be debugged and
-
0:36
managed when using operation in operation queues.
-
0:40
The main reason you want to use operation and operation queues
-
0:44
is because you want to manage what's being processed in the background.
-
0:48
Unfortunately, with GCD, you sense something to be
-
0:50
processed in the background and then you don't really have much control over it.
-
0:55
Whereas with operations, you have a lot of control over the tasks.
-
1:00
In our example, we're downloading images.
-
1:03
Ideally, we want to download only the images for cells that are visible.
-
1:08
This will make our app a lot more efficient and performant.
-
1:12
So let's see how we can replace GCD for image downloading and
-
1:16
use operations instead.
-
1:19
So coming back to our project.
-
1:21
As we had previously seen,
-
1:23
we had modified our ImageTableViewCell to use a Grand Central Dispatch.
-
1:29
We accessed the global concurrent queue and
-
1:32
used the asynchronous method to download the image data.
-
1:36
Now instead of using Grand Central Dispatch in this example,
-
1:40
we're going to use operations and operation queue.
-
1:44
Now an operation is a unit of work.
-
1:48
So whatever we performed here would be part of our operation,
-
1:53
and operation queue is synonymous to the DispatchQueue.
-
1:58
Basically, we'll add our operation to the operation queue.
-
2:01
Now, the great thing about Grand Central Dispatch is that it's
-
2:05
a block-based interface, right?
-
2:07
So, you just provide a block of code and you dispatch it to a queue.
-
2:13
Whether it's the main queue, the global queue, any queue.
-
2:16
With operation and operation queue, it's not so.
-
2:20
Because operation and operation queues are built on top of GCDs,
-
2:25
they are more object oriented-based.
-
2:29
You have to subclass an operation and
-
2:31
then provide it the unit of work that you want to perform.
-
2:35
And then of course, send it off into the operation queue.
-
2:40
So, let's create this subclass.
-
2:43
And we'll say a new file, a Swift file.
-
2:46
And we'll say, image.
-
2:49
We'll just call it an imageDownloader.
-
2:53
We're going to import UIKit.
-
2:59
We'll say class imageDownloader, which is a subclass of operation.
-
3:05
We're going to have a constant of type photo.
-
3:12
We're gonna declare a designated initializer, which takes in a photo
-
3:22
and sets it to our constant.
-
3:27
So, the next thing, where our unit of work is performed,
-
3:32
just like in our ImageTableViewCell, we had all of this
-
3:36
going on where we download the image data from a specific URL.
-
3:42
That's exactly what we're going to do inside our ImageDownloader.
-
3:46
But where do we do this?
-
3:48
Here, we had this convenient async method where you just provided a block of code.
-
3:54
Well, in the operation, you have this method call main,
-
3:58
which we have to overwrite.
-
4:00
If you command click here, you can look at what this definition looks like.
-
4:06
And here you can see this main method.
-
4:09
This is where our unit of work will be performed.
-
4:12
There are other methods that you can note, which is start, isCancelled.
-
4:16
You have a Cancel method.
-
4:18
Of course, you can query it for a status.
-
4:21
You have methods like isExecuting, isFinished, isConcurrent, all right?
-
4:27
So we have to override the main method.
-
4:31
So first and foremost, the compiler is complaining that we need to provide it
-
4:36
with an override directive because we are overwriting it from the superclass.
-
4:41
So the first and foremost thing we need to check, if isCancelled.
-
4:46
So if this operation is cancelled, then we simply return.
-
4:52
We don't need to do anything here.
-
4:55
So we're gonna create another constants in another constant.
-
4:59
We're going to call this imageData =
-
5:04
Data(contentsOf: url, photo.imageURL).
-
5:10
All right, so while this data is being downloaded,
-
5:14
it's entirely possible that this operation was canceled once again.
-
5:20
So if it has been canceled, then we're going to check if it's been canceled, and
-
5:24
then return.
-
5:26
Now the compiler is complaining.
-
5:27
Let's see what it's complaining about.
-
5:28
It says, cannot convert value.
-
5:32
Right, so we need to provide it a URL cuz that is a string.
-
5:35
So I'm going to convert that.
-
5:38
And it's still complaining.
-
5:43
I think we need to implicitly unwrap this.
-
5:47
And it's still complaining.
-
5:48
So that's the warning.
-
5:51
Right.
-
5:52
So the error is called can throw but it is not marked with try.
-
5:57
So the error is not handled.
-
5:58
So this initializer actually throws,
-
6:04
so we need to put in a try.
-
6:07
So we'll just say try?,which will give us an optional.
-
6:10
And then, if it successfully downloaded the data,
-
6:18
then we're going to unwrap it and
-
6:23
say if let data= imageData, data.length >0.
-
6:30
And that's complaining, and says lend has been renamed to count, right?
-
6:37
So will use counts.
-
6:40
So basically, we're just checking to see we actually got some data.
-
6:42
And if we did, then we're simply going to add it to our ImageCache.
-
6:48
So you add photo.imageURL.
-
6:54
So we need to add an image.
-
6:58
So let's convert this to a UIImage.
-
7:01
We'll say let image =.
-
7:10
All right, so so far we don't have we're not implicitly unwrapping anything.
-
7:16
And this code is pretty safe.
-
7:19
We have an error somewhere.
-
7:22
Right, okay, so we need to implicitly unwrap the image.
-
7:27
But here we're already checking to see if we actually have data.
-
7:30
And if we do, then we're creating an instance of image.
-
7:33
And then we're going to add it to our cache.
-
7:37
Great, so as you can see,
-
7:39
this is a lot more involved than using Grand Central Dispatch.
-
7:42
But as I mentioned, there's a lot of advantages to using operations and
-
7:47
operation queues.
-
7:49
One of them is you can query it for state or status, as we had seen here.
-
7:54
You can check if it's executing, finished,
-
7:56
it's concurrent, it's ready, all of those things.
-
7:59
You can check the state of your operation.
-
8:02
You can cancel your operation.
-
8:03
You can check how many operations have been queued.
-
8:06
So, all of this operation management is being done.
-
8:11
It is a lot of heavy lifting if you have a lot of
-
8:14
concurrent tasks going on within your application.
-
8:17
So you want to use all of this management layer that the operation queue and
-
8:22
operation provides you with.
-
8:24
Because with the dispatch async, once the work has been dispatched,
-
8:28
then you really don't have a lot of control over it.
-
8:31
All right, so going over to our ImageTableViewCell.
-
8:37
What we had here was in our configure method, we got our image from the cache.
-
8:42
And if we didn't have it in the cache, then we loaded from the network.
-
8:46
So we're going to do something very similar here.
-
8:47
We're going to use the operation that we just created,
-
8:52
the ImagDownloader operation.
-
8:55
So we're gonna create a property that simply says
-
9:00
ImageDownloader, Of type ImageDownloader.
-
9:07
And this will be an optional.
-
9:11
So instead of saying, load image, we'll say,
-
9:17
set up or configureImageDownloader(for photo).
-
9:30
So let's copy this method signature.
-
9:32
And we're going to create a new method that doesn't return anything.
-
9:40
And basically it takes in a photo of type Photo.
-
9:44
You have to put in a : here.
-
9:47
So we're gonna configure an ImageDownloader for a photo.
-
9:51
Sorry we're passing a photo here, all right.
-
9:53
So say let downloader = ImageDownloader,
-
10:00
and we pass in a photo, great.
-
10:06
Now what happens after it downloads a photo?
-
10:12
Just like here, once it downloaded a photo we set it to the imageView,
-
10:15
we have to do the exact same thing here.
-
10:17
So we'll say, downloader.completionBlock.
-
10:21
We can say once you have completed whatever operation you're doing,
-
10:26
in our case once you've downloaded the image, you have to do a bit more work.
-
10:32
Whenever you're doing work inside an operation,
-
10:35
you always have to check if that operation has been cancelled.
-
10:38
So you have to check to see
-
10:41
if downloader.isCancelled, then simply return.
-
10:48
If not, then do something.
-
10:50
Well, in our case, that something is setting the ImageView with the image.
-
10:56
Now, we could have created a property within downloader
-
11:00
to download it to an image property.
-
11:02
But since we're saving it inside our cache, because we're still using
-
11:06
that caching mechanism, we're just going to get it from the cache.
-
11:09
Now remember, the operations are being done on a background queue.
-
11:14
So if we're updating the UI, we have to do it on a main queue.
-
11:18
So I'm going to just copy this code here.
-
11:22
And I will paste it here.
-
11:24
Now, since we don't have the image, we're going to get it from our ImageCache.
-
11:27
So, we'll say, ImageCache.shared.get, oops.
-
11:34
And we'll pass it the photo.imageURL().
-
11:40
All right, so, we've configured our operation.
-
11:44
We have our operation property for our imageTableViewCell.
-
11:50
And we're no longer using this loadImage method, we can even comment it out here.
-
11:55
We'll just comment that out so there 's no confusion.
-
11:58
And then going back to our PhotosTableViewController.
-
12:02
Now to manage operations, we need an operation queue.
-
12:06
Now creating an operation queue is very straightforward.
-
12:10
We'll create a lazy property.
-
12:11
So we'll say lazy var downloadQueue is equal to,
-
12:17
well, is of type OperationQueue.
-
12:27
And I create a queue.
-
12:30
We'll create a variable called queue of type OperationQueue.
-
12:37
We're creating a lazy property here because we don't want it to be
-
12:42
instantiated unless it's necessary, or until it's time for it to be instantiated.
-
12:50
It's important that your name your queue so
-
12:52
that the name shows up in instruments or in debugger.
-
12:55
So we're gonna call this the Image Downloader Queue.
-
13:05
Now one thing we can do since we're debugging and
-
13:08
testing this operation queue out, we can set the maxConcurrentOperationCount to 1.
-
13:14
What this will do is it'll allow us to see the operations in
-
13:19
our queue finishing one-by-one.
-
13:22
Of course, you must leave this part out if you're building the application for
-
13:26
production.
-
13:27
Because you want to let the system handle how many operations it can do it at once.
-
13:34
So how do you decide how many operations it can run at once?
-
13:37
Well, that all depends on the hardware, it depends on the resources, it depends on
-
13:42
the number of cores in the CPU, on the amount of memory available at that time.
-
13:47
So, it's best for
-
13:49
the platform to decide how many number of threads it can create at once.
-
13:56
And we should not be making that determination.
-
14:00
And then, finally, returning the queue.
-
14:04
And we need the open and close parentheses so that it's going to execute this block
-
14:09
of code when we ask for our downloadQueue.
-
14:15
All right, so we've created the downloadQueue.
-
14:18
Lastly, we need to add our download operation to our downloadQueue.
-
14:24
So we're going to do that in our cellForRowAt indexPath method.
-
14:28
So after we have configured our imageTableViewCell,
-
14:33
we're going to check to see if we have a downloader.
-
14:38
So, we'll say, if let downloader = imageTableViewCell.ImageDownloader.
-
14:46
So, if we do have a downloader, we're going to add it to our downloadQueue.
-
14:52
So, we'll say, add operation(downloader).
-
14:57
All right, one thing that I forgot to do was in our imageTableViewCell.
-
15:02
Once this downloader has been configured, every time it dequeues,
-
15:06
it's still going to be configured.
-
15:09
So we need to set it to nil every time a imageTableViewCell is being dequeued.
-
15:17
Why is that so?
-
15:17
Because it's associated to that specific image.
-
15:22
Remember, the cells are being reused over and over again.
-
15:27
We don't create, if we have 1,000 JSON objects in our photos' JSON,
-
15:33
then that doesn't mean that 1,000 cells are being created.
-
15:39
At the most it's whatever amount of visible cells there are, like eight,
-
15:44
ten, which are being reused over and over again.
-
15:48
So, when we're reusing a cell, we want to make sure that the ImageDownloader
-
15:52
has been cancelled and set to nil.
-
15:55
So, we're gonna do self.imageDownloader?.cancel.
-
16:01
And then, self.imageDownloader = nil.
-
16:06
Great, so, we're reusing our ImageDownloader and
-
16:10
we're setting it to nil.
-
16:11
We're configuring it when necessary.
-
16:14
And we're adding it to the downloadQueue when we're retrieving the cell.
-
16:20
So, let's run our application to see if we successfully implemented operations and
-
16:25
operation queues.
-
16:30
And, it looks like something went wrong.
-
16:33
So, let's go back to our cell.
-
16:36
And we're checking to see if we get it from cache.
-
16:40
If not, we're configuring our ImageDownloader.
-
16:44
And we're creating this local variable called downloader, but
-
16:48
we're never setting it to our property.
-
16:50
[LAUGH] So, do not forget that folks.
-
16:55
So self.ImageDownloader = downloader.
-
16:58
Perfect, now lets run our application and see if this marvel works.
-
17:06
All right, we're not losing user activity.
-
17:10
So our user interface is responding to touch events and scroll events.
-
17:18
Now we can scroll.
-
17:20
And as you can see, this is responding pretty well.
-
17:25
It's executing the downloading in the background, and as cells become visible,
-
17:30
it is showing us the necessary photo that belongs to that particular cell.
-
17:36
And I'm scrolling pretty fast here.
-
17:40
And as you can see, there's no loss in frames.
-
17:44
There's no lag.
-
17:46
There's no stuttering. So there you have it,
-
17:48
we successfully implemented operation and operation queues,
-
17:53
which provided similar functionality to our Grand Central Dispatch approach.
-
18:01
Concurrency is a powerful tool to take advantage of the computing power of
-
18:06
modern multi-core CPU's.
-
18:07
GCD or operation queues make it easier to execute more code concurrently.
-
18:14
However, these technologies do not guarantee to make our apps more efficient
-
18:18
or responsive.
-
18:20
It is our responsibility as developers to know what is going on with our code and
-
18:25
the side effects we're introducing.
-
18:28
Before introducing concurrency, study your application's needs to understand where
-
18:33
and how introducing concurrency will benefit the performance and
-
18:37
responsiveness of your app.
-
18:39
It's not a be-all, end-all solution, but
-
18:42
it is yet another important tool in our tool belt as developers.
-
18:47
So make sure you understand it wisely and use it carefully.
-
18:51
When carefully implemented,
-
18:52
concurrency has great advantages to the performance of your application.
-
18:58
As always,
-
18:58
check out the teacher's notes to learn more about this amazing technology.
-
19:02
This is Ahmed signing out until next time.
You need to sign up for Treehouse in order to download course files.
Sign up