Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Cocoa-way of observing progress of a background task?

Imagine the following situation: you have a background task (the term "task" here means a random computational unit, not an NSTask!), that is implemented using any of the modern technology such as Grand Central Dispatch or Operation Queues. Some controller object on main thread wants to monitor the progress of this background task and report it to a user.

Task progress can have following characteristics:

  • Be indeterminate or determinate
    Because controller object must know when to switch NSProgressIndicator to the appropriate style. We can use a convention that progress is treated as indeterminate until the actual progress value raises from zero.
  • Progress value itself
    A simple float value
  • Localized description of a current phase
    NSString, because communication with user is good

What design suits these requirements at best while being the most Cocoa-ish?

There can be variants.

Delegation

Before firing up the task set your controller object as delegate.

@protocol MyBackgroundTaskDelegate
@required
- (void) progress: (float) value; // 0.0…1.0
@optional
- (void) workingOn: (NSString*) msg; // @"Doing this, doing that…"
@end

Actually, i successfully used this template many times, but it feels a little too verbose.

Block callback

Very similar to delegation, but keeps code in one place.

// Starting our background task...
[MyTask startComputationWithProgressHandler: ^(float progress, NSString* msg)
{
    // Switching to the main thread because all UI stuff should go there...
    dispatch_async(dispatch_get_main_queue(), ^()
    {
        self.progressIndicator.progress = progress;
        self.informationalMessage = msg;
    });
}];

KVO or polling of a progress properties

In this case background task object must have two properties similar to these:

@property(readonly, atomic) float progress;
@property(readonly, atomic) NSString* message;

And a client (our controller object) should set itself as an observer of these properties. The major flaw i see in this solution is that KVO-notifications always arrive on the same thread that caused the change. While you can force your observer (callback) method to run on a particular GCD queue it may not be always appropriate.

NSNotificationCenter

Background task sends notifications and client listens to them.

Is there any other patterns applicable to this situation? What solution can be treated as a most modern and Cocoa-ish?

like image 924
Konstantin Pavlikhin Avatar asked Sep 13 '12 09:09

Konstantin Pavlikhin


2 Answers

When it comes to What is the Cocoa-way of observing progress of a background task? I would say delegation and NSNotificationCenter because blocks and KVO were introduced later, and hence didn't originally exist in the first Cocoa code writting years. In fact optional protocol methods were not present in previous objc versions too, everything was required by default.

From that you can actually see that blocks are a simpler way of implementing adhoc delegates, where the receiver of the block declares what parameters are passed to the block, and you are free to do whatever you want with them in your block. And KVO seems to be a less boilerplate way of implementing NSNotification with a more standardized approach to properties, useful for joining the UI created in what previously was called Interface Bilder, and simplifying the "what the hell do I have to do to know when this value changes" which requires a lot of documentation with NSNotification and long constants.

But I still think that there are places for each of these techniques: blocks are nice for mini-adhoc protocols, but would be a serious bother if you need a medium or higher interface area or bidirectional interface, and KVO doesn't help with watching global variables or values outside of a class/object, or stuff you don't want to make part of your public interface.

So my definitive answer is:

  • 1 to 1 simple communication: blocks
  • 1 to 1 complex communication: delegates/protocols
  • 1 to many simple communication: KVO (where possible)
  • 1 to many complex communication: NSNotifications

As always, pick the best tool for each problem, and consider I'm guilty of implementing all of the above in none of the suggested ways!

like image 71
Grzegorz Adam Hankiewicz Avatar answered Nov 19 '22 23:11

Grzegorz Adam Hankiewicz


For the type of task you describe, I feel that NSNotificationCenter is the best option for a generic pattern. The reason is that you can't know, generally, how many external observers there are. The notification system already supports an arbitrary number of observers for an event, whereas the other non-polling options (delegation and blocks) are more typically one-to-one unless you do extra work to support multiple registrations.

As you pointed out yourself, polling is a bad idea if you can avoid it.

like image 1
Steve Madsen Avatar answered Nov 20 '22 00:11

Steve Madsen