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:
What design suits these requirements at best while being the most Cocoa-ish?
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.
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;
});
}];
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.
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?
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:
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With