开发者

ways to bind model classes in Objective-C

(I hope someone will correct my terminology if I get it wrong -- I'm still sorting out terms)

I have a series of classes in my model. I need to get some data from a url (SatDataGetter) and combine it with a location and date specific calculation (DayCalculater), do some further calculations (DataMixer), some interpretation to make it user-understandable (Advisor), and then present the results in a view.

There are issues with setting up the dependencies and making sure that, for instance, the SatDataGetter has valid data before it gets called by DataMixer, before it gets called by.. yo开发者_StackOverflowu get the idea. Of course, if the location changes, I need to update the whole thing from the bottom up. At minimum I have to get a message to the ViewController and the Advisor to reload their data.

The research I've done suggests that NSNotification is one way to go, but I could also try Key-Value Observing. I found some old posts (2009) on KVO, suggesting some possible problems and difficulties with debugging. http://www.mikeash.com/pyblog/key-value-observing-done-right.html

What's the preferred method? What are the issues I should be considering in deciding -- For instance: The SatDataGetter essentially returns a single number. KVO seems like a reasonable way for DataMixer to keep track of what that value is, but I don't think I want all the parent classes to be doing KVO on the dependent variables.

When do you choose an NSNotification and when KVO?


The difference between NSNotifications and Key-Value Observation is largely one of coupling, but there are also performance implications.

Anyone can subscribe to your NSNotifications. All they need to know is the string/key that you notify under. They don't need to know anything about your classes/object-graph etc. So when you want to notify a world that doesn't know about the details of your class, NSNotification is the way to go. For instance, if you're vending a framework to other developers, it's probably better to notify by NSNotification than by exposing the internals of your framework to the degree that might be necessary to allow consumers to Key-Value Observe your objects.

In order to KVO observe an object you first have to be able to get a reference to it, which is not strictly true of NSNotifications (but usually happens to be true in my experience.) Secondly, you need to know enough about the implementation to know what to observe. With NSNotification the notifier need only publish a notification string/key. With KVO, you need to know the name of a property of the object. Sure, someone could publish static strings and tell you "you can KVO me for these properties" but that effectively becomes an API contract that may be harder to maintain going forward. (Say, for instance, you want to remove that property in a future version -- then you have to rig up other things to continue to send those notifications and provide values when people call valueForKey: -- in short, once you do that, you can never change that property.)

The other thing to remember with these various degrees of coupling is that with KVO, there can be an expectation that the observer knows about the details of your classes/objects. After all, they're registering a very specific interest in your object; they claim to know what that means -- how it works. Therefore you might expect them to be sensitive to the performance implications. With NSNotifications, the consumer can observe your notification knowing virtually nothing about you, and may not be aware of performance implications of how they act in response to the notification.

The drawback shared between the two approaches is that, absent extra work, they're both delivered synchronously. The notifying object is at the mercy of what the observer chooses to do (synchronously) when it receives the notification. The two mechanisms differ here in that for NSNotification it's quite easy for the notifying object to use performSelector:afterDelay: to cause the notification to be sent "asynchronously" (with respect to the call that spawns the notification) on the next pass of the run loop (see also NSNotificationQueue). This is not as readily possible with KVO. This difference alone may be crucial in certain situation.

Generally, I find that the loose coupling of NSNotification lends itself to events that are either large-granule (i.e. potentially represent a large group of changes) or relatively infrequent. KVO notifications are, by their very nature, fine-grained. You're explicitly observing a change of a single property (per registration) on a single object. This has the potential to blow up the number of registrations and the number of notifications. Every observation has a performance cost to it.

Another shared drawback to both of these is debuggability. I mentioned in a comment above that KVO can be a challenge to debug, and it shares that challenge with NSNotificationCenter as well.

The key difference between these two and the delegate pattern is that delegate relationships are typically 1:1 (one delegate.) (Sure, you could have an array of delegates, but that's pretty uncommon, and debatably an anti-pattern.) NSNotification and KVO are both inherently 1:N (many observers.)

In closing, I always like to say, "use the highest level of abstraction that will get the job done." If you only need a 1:1 relationship, use a delegate. For 1:N notifications, if an NSNotification will work -- in other words if the desired coupling is low and/or the notifications are large-grained or infrequent, use NSNotifications. If the coupling is tight, or the need is fine grained, use KVO. Also remember that you can adapt a delegate pattern from 1:1 to 1:N by having the delegate send NSNotifiations. With NSNotifications and KVO, you can't really control who observes you or how many observers you have.


I generally use KVO to watch for changes to properties within an object - so, instead of overriding the setter for an object, I'll use KVO to fire the observe method on a change, and then call other methods at that point.

I tend to use NSNotifications to inform other objects that something has happened. In your example, I may have a singleton to handle the location stuff, and then send a notification when userLocation changes.

And, of course, you can also use the delegate pattern to notify another object of a change.

These aren't hard and fast rules though...I'm wont to change on a whim!


You probably don't need any of those strategies, you can probably get by with some sort of delegate hierarchy. If you are using core location or any other service that you can't get reliable data from synchronously, then you can use that as your starting point... if you have 2 such services, (getting http data, and Core Location for instance), Then you may have to perform some caching and delegation... i.e. last best location + current http data get pushed to the view.


How about NSOperations?

// create two ops, and set a dependency
DownloadOp *op = [[Download alloc] initWithURL:@"http://xxx"];
CalculationOp *op = [CalculationOp new];
[calculationOp addDependency:downloadOp];
// ... more steps

// add to the queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:downloadOp];
[queue addOperation:calculationOp];

// runloop for unit testing
while(!parserOp.isFinished) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}

Inside the -[CalcuationOp start]:

    // get the result from the previous step
    id obj = [self.dependencies objectAtIndex:0];
    if ([obj isKindOfClass:[DownloadOp class]]){
        DownloadOp *op = (DownloadOp*) obj;
        self.input = op.output;
    }

Inside the AdvisorOp:

  // update the GUI from the main thread
  dispatch_queue_t mainQueue = dispatch_get_main_queue();
  dispatch_async(mainQueue,^{
    // update some UI element
 });

Then listen to location updates on the CLLocationManager delegate to start the whole thing. I'm using this with the logic decoupled from the operations so I can unit test each part alone.
Benefit: it runs asynchronously and you can cancel the operations on the queue at any time. Downside: you have to learn NSOperation.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜