Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

iOS Communication Patterns Explained, Part V: Blocks

DZone's Guide to

iOS Communication Patterns Explained, Part V: Blocks

Blocks are quite a powerful tool and technique in Objective-C, and you can see an extensive usage of the blocks in Apple’s own frameworks.

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

Finally, we are here! It's been quite a long road since the initial solution, where we just put all of our code into one class and we were happy that it worked in the current situation, where our code was based on the delegation pattern. In this blog post, we go further. 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 one-to-one communication situations, like we have in our example code, using blocks is a really good decision.

About Blocks

Hopefully, you have already heard about blocks. They are quite a 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 a standalone entity. This block of code could have parameters or could return a result based on the logic of the block. They are quite similar to functions, but blocks don’t need to have any name defined. Moreover, a block can be assigned to a variable. This means that our code can be passed to a function or a method, even in another class, and can be executed by 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

The first one is 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 because, 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 the case of blocks? Blocks should be considered as a separate entity, a kind of new context. If you pass a block containing a 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 the block and visible by the block are read only. That means that the block cannot modify them. In order to change this behavior, we need to define the variables with the __block keyword.

My Solution in UML

After this warm-up, let's see the UML diagram of the proposed solution:
Wait!

Haven’t we seen this 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 thing about the blocks: you can go back to the simple solution and sacrifice a little bit complexity on the block.

What Could Go Wrong?

There are a few (actually a lot) of things that can go wrong. Let's look at a few.

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 properties of the self. If I use self with the block, I will create a nice retain cycle. To avoid 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.

Background Thread

This topic is less obvious with our current project but could cause some facepalms. Here's 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 adding an NSOperation to the main queue and executing the block code on the main queue.

Code Changes

The code is getting cleaner and shorter. Let’s go through all of the files that have been changed.

First of all, we can delete the PMODataHolder.h and 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 PMOPictureController.h:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface PMOPictureController : NSObject

/**
The designated initializer, the picture's url is mandatory to pass.
@param url The picture full url, as NSURL
@return An instance PMOPictureController.
*/
- (nonnull instancetype)initWithPictureURL:(nonnull NSURL *)url NS_DESIGNATED_INITIALIZER;


/**
Start the asynchron download of the image.
*/
- (void)downloadImage;

/**
A high level, public API function to retrieve the image as UIImage
@return an instance of UIImage.
*/
- (nullable UIImage *)image;


/**
Removing the default initalizer
@return nil
*/
- (nullable instancetype)init NS_UNAVAILABLE;

@end

As you can see, I removed the import statement for the PMODataHolder.h and also removed the protocol from the end of the interface line.

PMOPictureController.m:

#import "PMOPictureController.h"
#import "PMODownloader.h"
#import "PMOPictureWithURL.h"
#import "PMODownloadNotifications.h"

//1
typedef void (^DownloadCallBack)(NSData *);


@interface PMOPictureController()

/**
Our private data class, storing and hiding the information.
*/
@property (strong, nonatomic, nullable) PMOPictureWithURL *pictureWithUrl;

@end

@implementation PMOPictureController



#pragma mark - Initializers
- (instancetype)initWithPictureURL:(NSURL *)url {

self = [super init];
if (self) {
_pictureWithUrl = [[PMOPictureWithURL alloc] initWithPictureURL:url];
[self addObserverForDownloadTaskWithDownloader];
}
return self;
}


#pragma mark - Public API
- (void)downloadImage {
//2
PMODownloader *downloader = [[PMODownloader alloc] init];
__weak __typeof__(self) weakSelf = self;

DownloadCallBack downloadCallBack = ^void(NSData *downloadedData) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"Picture downloaded");
[weakSelf willChangeValueForKey:@"image"];
weakSelf.pictureWithUrl.image = [UIImage imageWithData:downloadedData];
[weakSelf didChangeValueForKey:@"image"];
}];
};

[downloader downloadDataFromURL:self.pictureWithUrl.imageURL completionHander:downloadCallBack];
}

#pragma mark - Accessors
- (UIImage *)image {
return self.pictureWithUrl.image;
}


#pragma mark - Notification Events
- (void)didImageDownloadFailed {
NSLog(@"Image download failed");
}


#pragma mark - Notification helpers
- (void)addObserverForDownloadTaskWithDownloader {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didImageDownloadFailed)
name:PMODownloadFailed
object:nil];
}


- (void)removeObserverForDownloadTask {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}


#pragma mark - Dealloc
- (void)dealloc {
[self removeObserverForDownloadTask];
}

@end

At 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 instance from that type. Honestly, Apple gave us a lot of ways to define a block, and sometimes it is really confusing. My favorite website 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 //1.

The first thing I did here is adding [NSOperationQueue mainQueue] addOperationWithBlock: to the main thread. The NSURLSession in the PMODownloader will use a background thread and queue in order to download the picture. For 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.

Of course, we need to pass the block to our downloader. I named it completionHandler.

PMODownloader.h

#import <Foundation/Foundation.h>

@interface PMODownloader : NSObject

//1
/**
The download method, which triggers the download from the parameter url, and executes the passed block in case of the succesful download.

@param url the URL of the downloadable resource
@param callback the callback block, which will be executed with succesfull download
*/
- (void)downloadDataFromURL:(nonnull NSURL *)url completionHander:(void(^_Nonnull)(NSData * _Nullable downloadedData))callback;

@end

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 //1  .

PMODownloader.m
#import "PMODownloader.h"
#import "PMODownloadNotifications.h"

@implementation PMODownloader


#pragma mark - Public API / Protocol implementation
- (void)downloadDataFromURL:(nonnull NSURL *)url completionHander:(void(^_Nonnull)(NSData * _Nullable downloadedData))callback {

NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];

NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
[self notifyObserverDownloadFailure];
} else {
//2
callback(data);
}
}];
[task resume];

}


#pragma mark - Notifications
- (void)notifyObserverDownloadFailure {

[[NSNotificationCenter defaultCenter] postNotificationName:PMODownloadFailed
object:self
userInfo:nil];
}



@end

At //2 there is the block magic. Actually, it is just one line of code that 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 as to why this solution is more powerful and uses fewer dependencies. Actually, I can use PMODownloader for any task where I want to download something, and I only need to pay attention to the block signature. If the downloaded file is not an image, but an XML or JSON, I can start to serialize 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.

Conclusion

My series is now finished. I hope you have discovered at least one useful piece of information during these 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 one-to-one communication, KVOs for broader auditions 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.

Feel free to comment!

Check out the other articles below.

iOS Communication Patterns Explained, Part II: NSNotifcationCenter

iOS Communication Patterns Explained, Part III: Key-Value Observing

iOS Communication Patterns Explained, Part IV: Delegation

Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:
mobile ,ios ,blocks ,communication patterns

Published at DZone with permission of Peter Molnar, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}