Finally, we are here;). The road was quite long from the initial solution, where we just put all of our code into one class, and we were happy that it worked to the current situation, where our code based on the delegation pattern. In this blog post we go further, and we will apply the block based solution for our small downloader class.
In the last post we applied the delegation pattern to our small app. For the 1-to-1 communication situations, like we have in our example code, using the blocks is a really good decision.
Hopefully you have already heard about the blocks, it is a quite powerful tool and technique in Objective-C, and you can see an extensive usage of the blocks in Apple’s own frameworks. The main concept of the blocks is that you can define a block of code as standalone entity. This block of code could have parameters, could return a result, based on the logic of the block. It is quite similar to the functions, but blocks don’t need to have any name defined. Moreover block can be assigned to a variable. It means that our code can be passed to a function or a method, even in another class, and can be executed from the other entity. It is good to mention that the receiver class, which has received the block as a method parameter doesn’t even know about the sender class. Which means that those classes really loosely coupled. Isn’t it powerful? Yes, it is. With some caveats.
Retain cycles and variable scopes
Here is the first one: the retain cycle. I hope you have already heard the expression, mostly about the strong/weak property parameters. In a nutshell: if you create strong references mutually between two objects, you will end up with a retain cycle, since during the deallocation of any of the objects the ARC won’t enable to free the memory of the object while there is still a strong reference pointing to the object. The result is a memory leak.
Why is it important in case of blocks? Blocks should be considered as a separate entity, a kind of new context. If you pass a block, containing reference to self to another object, that reference for the original object will be strong. Voila, you have just created a retain cycle.
There is another interesting behavior of the blocks: by default the variables defined outside the scope of block, but visible by the block are read only. That means that the block can not modify them. In order to change this behavior, we need to define the variables with the
My solution in UML
Haven’t we seen it before? Yes, you are right, this is the same UML where we started, except the completionHandler parameter in the method of PMODownloader. This is the beautiful about the blocks, you can go back to the simple solution, and sacrifice a little bit complexity on the block.
What can go wrong?
There are a few (actually a lot :P) things can go wrong, let see at least 2 things.
Retain cycle and weakSelf
First of all the retain cycle I have just mentioned above. Usually it happens when you pass a reference type (an object) to another object. In this case we want to update one of the property of the
self. If I would just use self in the block, I have just created a nice retain cycle. To avoid from that we should use the weak version of the self, before using and declaring the block. Something like this:
__weak __typeof__(self) weakSelf = self;
After this declaration we can use weakSelf safely in the block.
This topic is less obvious with our current project, but could cause some facepalms. Here is the thing: The NSURLSession usually uses a background thread in order to download your data. When it is done, it will call you the recently created block… Still on the background thread. Which could be great, but if you want to update your User Interface with the changes you have to use the application main thread. For that reason I would suggest to add an NSOperation to the main queue, and execute the block code on the main queue.
The code is getting cleaner and shorter. Let’s go through all of the files, which has been changed.
First of all we can delete the
PMODownloaderFromURL.h files, which were our protocol definition files.
Let see what we need to change in our other files:
We definitely need to change our
As you can see I removed the import statement for the PMODataHolder.h and also removed the
//1 there is a forward declaration for our block. As you can see you can easily declare your block as a type, so later on in your code you can easily create a kind of an instances from that type. Honestly, Apple gave us a lot of ways how we can define a block, and sometimes it is really confusing. My favourite web side to help me is http://goshdarnblocksyntax.com. Feel free to use it. As you can see the delegate property reference and codes were removed as well. First of all there is the weakSelf definition, and after that the actual block is defined pretty much same as any other variable (DownloadCallBack downloadCallBack =), since the type is already declared at
The first thing is here what I did is to with to the main thread with
[NSOperationQueue mainQueue] addOperationWithBlock:. The NSURLSession in the PMODownloader will use a background thread and queue in order to download the picture. For the safety I switched to the main thread with the KVO update and update of the image by adding those operations as a block to the execution queue of the main thread.
And of course we need to pass the block to our downloader. I named it a completionHandler.
I removed the reference and the import statement for the PMODownloaderFromURL protocol from the header, and the only public API method has been updated with a possibility to receive a block as a parameter at
//2 there is the block magic. Actually it is just one line of code, which is calling back the block on the original object, with the downloaded raw data as a parameter. I hope I don’t need to explain more, why is this solution is more powerful and using less dependencies? Actually I can use PMODownloader for any task, where I want to download something, and I need to only pay attention to the block signature. If the downloaded file is not an image, but an XML or JSON, I can start to serialise and parse them in the block, but from the PMODownloader it is invisible, and it won’t need any further change.
What about the Unit Tests?
Since I changed before the test for PMOPictureController to a KVO event, which Key-Value change is done on the PMOPictureController, and our block based solution still triggering that observable event, we don’t really need to change anything in our test case.
My series finished with that post. I hope you have discovered at least one useful piece of information during those chapters. As you can see from the final code, usually you end up using more than one communication pattern. Even this small example I use blocks for the 1:1 communication, KVO for broader audition, to inform other classes that the image is available, and the Notification Center in the case of there is an error during the download.
You can download the project from here, and by switching branches you can switch between the different solutions: https://github.com/petermolnar-hu/CommunicationReFactory
Feel free to comment!