Showing posts with label Blocks. Show all posts
Showing posts with label Blocks. Show all posts

Sunday, May 6, 2012

Reverse Segues

UIStoryboards make transitioning between views very easy. However, segues work strictly in one direction. I have been asked several times what is the best way to send information back to the previous view controller. Unfortunately, there is no such thing as a reverse segue. In response, I have found four clean, useful patterns to create the needed behavior:
  1. Delegate
  2. Block
  3. Shared memory
  4. NSNotificationCenter
Each one has its advantages and disadvantages. I will discuss each one in turn.

Delegate

Summary

Cocoa uses delegates everywhere, so why not here too?

Code Sample

Let's say view controller X pushes view controller Y via segue. Y specifies a delegate protocol that it will call to pass the needed information. X conforms to that protocol and sets itself as Y's delegate. When Y is dismissed, it calls the delegate method.

// In view controller X
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    [[segue destinationViewController] setDelegate:self];
}

- (void)objectUpdated:(id)object {
    // Do something with the object.
}

// In view controller Y
@protocol MyDelegateProtocol
- (void)objectUpdated:(id object);
@end

- (void)viewWillDisappear:(bool)animated {
    [super viewWillDisappear:animated];

    [[self delegate] objectUpdated:[self object]];
}

Pros/Cons

Delegation is easy enough to implement. However, in this case, the delegate protocol probably has just one method. This hardly seems worth the effort to create the protocol. Because of this, I do not recommend delegates. I instead favor the next option: blocks. If you need more than one callback, a delegate may be more appropriate.

Blocks

Summary

Blocks are great for making callbacks and can easily replace any delegate.

Code Sample

Now when X pushes Y, it also gives Y a block. When Y finishes, it calls the given block and hands it the information to pass on. The block can then do whatever it needs with that information.

// In view controller X
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    [[segue destinationViewController] setBlock:^(id object) {
        // Do something with the object.
    }];
}

// In view controller Y
typedef void(^MyBlock)(id object);

- (void)viewWillDisappear:(bool)animated {
    [super viewWillDisappear:animated];

    if (self.block)
        self.block(self.object);
}

Pros/Cons

Blocks have two great advantages over delegates. First, they don't require a protocol. For convenience, you may write a one-line block typedef, but that is all. Second, blocks keep code where it is relevant, rather in a different method. For most cases, you should favor blocks.

Shared Memory

Summary

Sometimes you may push a new view so it can edit an object. In this case, you can avoid a callback entirely.

Code Sample

When pushing Y, just pass it a pointer to the object of interest. Any changes Y makes to the shared object will directly affect X.

// In view controller X
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    [[segue destinationViewController] setObject:[self object]];
}

Pros/Cons

This solution is the simplest of the four. However, it may not be suitable in some situations. One place this pattern doesn't do well is if you want view controller Y to have a Done and Cancel button. You would need to undo all the changes made to your object if the Cancel button is pressed. This could be made easier by using an NSUndoManager, but that may be overkill for what you want.

There is one particular place that this pattern is especially useful. When using Core Data with iOS 5, you may want view controller Y to have an NSManagedObjectContext that is a child of view controller X's NSManagedObjectContext. All you need to do is create the MOC in X, set the MOC's parent, and pass it on to Y.

NSNotificationCenter

Summary

Using NSNotificationCenter to pass around information is the least used option and the most work, but it can have some nice advantages worth mentioning.

Code Sample

Rather than have X tell Y it is interested in its information, X lets NSNotificationCenter know it is interested in an event.

// In view controller X
- (void)viewDidLoad {
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                                                          selector:@selector(consumeNotifcation:)
                                                                              name:MyNotification
                                                                             object:nil];
}

- (void)viewDidUnload {
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                                                    name:MyNotification
                                                                                   object:nil];
    [super viewDidUnload];
}

- (void)consumeNotification:(NSNotification * notification) {
    // Grab the information from the notification.
}

// In view controller Y
NSString * const MyNotification = @"MyNotification";
NSString * const MyObject = @"MyObject";

- (void)viewWillDisappear:(bool)animated {
    [super viewWillDisappear:animated];

    NSDictionary * userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                                [self object], MyObject,
                                                nil];

    [[NSNotificationCenter defaultCenter] postNotificationName:MyNotification
                                                                                           object:self
                                                                                       userInfo:userInfo];
}

Pros/Cons

Obviously this is a lot more code than the other options. It comes with two key advantages. First, this pattern makes multiple, unrelated observers very easy. Second, it allows for looser coupling between views. X and Y (and any other views/non-views) essentially don't know anything about each other. They simply send or receive information through NSNotificationCenter. One place I use this pattern is with an app-wide settings view. When the settings are changed, different parts of the app may need to know immediately when the change is made. For example, I may have a setting that can turn off downloading over the cell network. Both the networking code and the UI will need to know about this change. The notification makes sure that they are informed immediately without needing to hard code the interactions.

Conclusion

When passing information to a previous view controller, favor blocks. However, keep the other options in mind. They have their advantages too.

Other Information

For more information on UIStoryboards, check out my best practices.

Saturday, September 17, 2011

Lockless Exclusion Accessor Pattern

With all the work I have been doing with blocks and Grand Central Dispatch (GCD), I have developed a useful design pattern for handling mutual exclusion. I haven’t seen any patterns quite like this one, so I’ll claim it as my own.

Mutual exclusion is always a challenge when multithreading any application. GCD makes mutual exclusion substantially easier by using queues instead of locks. Nevertheless, there are still some challenges. When creating shared data I often include a warning in my documentation that it must only be accessed within a certain serial GCD queue. However, other developers that use my code may not read my documentation. Even I forget occasionally that some particular data isn’t thread safe. So, how do we make this easier? This is a situation for what I call the Lockless Exclusion Accessor pattern. I’ll call it LEA for short. (I also thought of calling it a “letter” since it’s closely related to a getter, but that seemed too silly and generic.)

Before going into the details of LEAs, we need to first understand why encapsulation is important. Most object-oriented languages have the ability to make variables private so they can only be directly accessed internal to the class. Limited access is given to the outside through getters and setters. This prevents classes on the outside making arbitrary changes to another classes internal data. To see how to encapsulate @propertys in Objective-C check out my @property article. We will apply the same principles to our shared data.

First, move your shared data into a class extension so it will not be externally visible. Second, you will want to typdef your block type. This keeps your code cleaner and easier to read. For this example, I’m going to use an NSManagedObjectContext as my shared data. So, my block typedef looks like this:

typedef void(^RBMOCBlock)(NSManagedObjectContext * moc);

Next, you need to create an accessor method. Outside classes will only be able to access your shared data through this method. It will look something like this:

- (void)accessMOCAsync:(RBMOCBlock)block {
        dispatch_async(dispatch_get_main_queue(), ^{
                block([self managedObjectContext]);
        });
}

The dispatch_async takes the passed in block and runs it on the main queue. We could (and really should) modify this code to run on a private serial queue instead. We also pass the shared data to the block. This grants the block exclusive access to that data (as long as the dispatch queue is a serial queue). You’ll also notice that this is an asynchronous call. If needed, we can also make a synchronous LEA. It looks nearly identical:

- (void)accessMOCSync:(RBMOCBlock)block {
        dispatch_sync(dispatch_get_main_queue(), ^{
                block([self managedObjectContext]);
        });
}

Now that we’ve built our asynchronous and synchronous LEAs, let’s use one.

[object accessMOCAsync:^(NSManagedObjectContext * context) {
        // Critical code goes here.
}];

That’s all there is to it. Just wrap your critical code in a block and you get exclusive, local access to the NSManagedObjectContext. LEAs are perfect for Core Data MOCs since they are not thread safe and are often the cause of much grief.

LEAs are also quite flexible. For example, you can merge multiple LEAs into one like this:

[object accessMOCAsync:^(NSManagedObjectContext * context, NSDictionary * userInfo) {
        // Critical code goes here.
}];

The above LEA gives the block exclusive access to two shared variables at once. You can extend this as far as you want. This way to don’t need to write multiple LEAs per class and you don’t need to nest LEAs. However, this comes with a tradeoff. You gain convenience at the cost of more contention for shared data.

Now, LEAs don’t perfectly protect shared data. It’s as good as Objective-C can get though. Rogue developers could pass the pointer to the MOC out of the block. They could also use the dynamic runtime to call the internal getters and setters. However, any developer doing either of these things is asking for a lot of trouble and should not be permitted to write Objective-C code.

For more GCD and block design patterns, check out these sources.


UPDATE:

I've removed all references to SyncSafe due to problems I've discovered.