Need some tips on Cocoa MVC / KVO templates

This is a very broad / vague question, but here. I apologize in advance.

The application (desktop application) that I create uses different types of input to generate a QR code (I just build it to learn Obj-C / Cocoa). The user can switch between different views that allow you to enter text text (one text field), VCard / MeCard data (several text fields) and other materials. Regardless of the input, the result is a QR code.

To save everything, I would like to use representations as view controllers, so they process their own inputs and can simply "send" general "data to encode" an object containing all the data to a central encoder. That is, a data object with the text of the text field will be created in the text view, and the VCard / MeCard view will use all its fields to create structured VCard / MeCard data.

I can bind all this together manually in the code, but I would really like to know how bindings / KVOs can help me. Alas, after reading the Apple developer docs and the simpler tutorials / examples I could find, I'm still not sure how to apply it to my application.

For example: A user edits text fields in a VCard window. The VCard view controller is notified of each update and "recounts" the data object. The central controller of the encoder is then notified of the updated data object and encodes the data.

The point of all this is that input representations can be created completely independently and can contain all types of input fields. They then process their own inputs and โ€œreturnโ€ a common data object that the encoder can use. Internally, the gazes observe their inputs to update the data object, and externally, the encoder only needs to observe the data object.

The problem is that I have no idea how to do this and keep it untied. Should there be an object controller between input-representation and its fields? Should there be a different one between presentation and encoder? What do I need, where? If anyone has a link to a good tutorial, share it.

Again, I can collapse my own notification system and glue code, but I think this should be avoided.

+4
source share
1 answer

Definitely a vague question, but one new to another, I feel your pain :)

I downloaded and unpacked each individual example and often ironed them. I found that this is the most valuable thing that will make me deal with the hump. I definitely recommend not to give up examples. I hacked this script to download and unzip them all.

In terms of good KVO structures, I found the technique described here to be very useful. This does not work as-is in Objective-C 2.0. Nor does it provide details of how it is used. Here is what I got:

KVODispatcher.h :

 #import <Foundation/Foundation.h> @interface KVODispatcher : NSObject { id owner; } @property (nonatomic, retain) id owner; - (id) initWithOwner:(id)owner; - (void)startObserving:(id)object keyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options selector:(SEL)sel; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; @end 

And KVODispatcher.m looks like this:

 #import "KVODispatcher.h" #import <objc/runtime.h> @implementation KVODispatcher @synthesize owner; - (id)initWithOwner:(id)theOwner { self = [super init]; if (self != nil) { self.owner = theOwner; } return self; } - (void)startObserving:(id)object keyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options selector:(SEL)sel { // here is the actual KVO registration [object addObserver:self forKeyPath:keyPath options:options context:sel]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // The event is delegated back to the owner // It is assumed the method identified by the selector takes // three parameters 'keyPath:object:change:' objc_msgSend(owner, (SEL)context, keyPath, object, change); // As noted, a variation of this technique could be // to expand the data passed in to 'initWithOwner' and // have that data passed to the selected method here. } @end 

Then you can register to watch these events:

 KVODispatcher* dispatcher = [[KVODispatcher alloc] initWithOwner:self]; [dispatcher startObserving:theObject keyPath:@"thePath" options:NSKeyValueChangeNewKey selector:@selector(doSomething:object:change:)]; 

And in the same object that performed above, you can have this method:

 - (void) doSomething:(NSString *)keyPath object:(id)object change:(NSDictionary *)change { // do your thing } 

You can have as many doSomething methods as you like. As long as they use the same parameters (keyPath: object: change :), this will work. With one dispatcher per object that wants to receive any number of notifications of changes in any number of objects.

What I like:

  • You can only have one observeValueForKeyPath for each class, but you can observe several things. The following thought is natural: "Hey, maybe I can pass the selector."
  • Oh, but it is not possible to pass multiple arguments through performSelector unless wrapper objects such as NSNotification . Who wants to clean wrapper objects.
  • Overriding observeValueForKeyPath , when the superclass also uses KVO, makes all common approaches tough - you need to know which notifications go to the superclass and which should be preserved.
  • Who wants to observeValueForKeyPath the same universal selector observeValueForKeyPath in every object? Itโ€™s better to just do it once and reuse it.

A good option would be to add one more field of id additionalContext type to KVODispatcher, and this extraContext object is passed in the objc_msgSend call. It may be useful to use it to hide the user interface object that needs to be updated when the observed data changes. Even possibly NSArray.

+2
source

All Articles