views:

1151

answers:

4

Consider the following common situation:

You have some MainView in your Cocoa application, loaded from a NIB, which is controlled by a MainViewController. Your MainView contains some controls, such as a UILabel infoLabel. You also have a delegate MyDelegate class which receives some sort of event.

You would like to make sure that when MyDelegate receives its event, the infoLabel is updated appropriately. However, the problem is that MyDelegate does not have a reference to the MainView or the MainViewController and does not know about the label.

One solution is to pass a MainViewController reference to the delegate object, but this feels wrong because you might find yourself in the undesirable situation where the object has each other's references.

What is the proper design to solve this problem?

A: 

The usual pattern is to, as you said, pass a pointer to MainView through its delegate methods. So, if MainView is calling its delegate's doSomethingWithFoo: method, you want to change that method to:

- (void)mainView:(MainView *)view doSomethingWithFoo:(id)foo

and call the new method accordingly. If you operate directly on the MainView pointer, you shouldn't have any problems with circular references.

Matt Ball
But suppose the delegate is not a delegate of MainView, but of some other protocol...?
Jake
Then use the method specified in the protocol? It's receiving some notification from the MainView, right? Perhaps there's some relevant aspect of the situation that's missing from the question.
Chuck
Suppose I set up an object that implements CLManagerDelegate protocol (Cocoa Touch) and listens for location updates. It does not receive notifications from MainView, yet needs to access the label.
Jake
Okay, it seems I misunderstood what was meant by "delegate object." Just a suggestion for next time, you only want to refer to something as a delegate if it actually is a delegate for the object in question. If I have a class ``TextController`` which is the delegate for a ``UITextField``, and I was talking about some other control in the app, I wouldn't refer to ``TextController`` as a delegate in that context.An app can have unlimited "delegates" in it, and the word itself is pretty much meaningless. Being a delegate __for what instance/class__ is the meaningful context.
Matt Ball
A: 

We implemented a tricky "Data"-object which controlls pretty much everything. It checks if there are changes and keeps all globals updated.

When creating new instances I refer to the Data class like this:

[[Button alloc] initWithData:data]];

Where data is the singelton Data-class. Now we can check if there are changes needed to react on.

I admit, it still requires referencing like you described though. There doesn't seem to be a simple parent reference imbedded.

Kriem
+1  A: 

Here are 2 options that come to mind:

  • If the event going to delegate is send by your controller, you can have that method return a value to the controller to push to the view.

  • You could also bind the infoLabel's value to some key (using, say, Cocoa bindings or just raw Key-Value Observing). The bound object (which could be the delegate, or some other model object) could just update the bound key, which would push a value to the infoLabel. As an example, you could bind the delegate's "info" member to the infoLabel's value. When the delegate receives an event, it can update the info member, and the view changes. The actual binding itself could happen in IB (if your delegate is in the nib) or in the controller (which has a reference to the view and the delegate.)

The latter solution is basically a circular reference, but one which seems somehow cleaner to me.

Jesse Rusak
Yes, this is certainly the cleanest solution so far.
Jake
+2  A: 

In an unnamed developer forum, someone writes:

So, to make a long story short, I have decided that I will start making use of NSNotifications. The Stanford course online that people have been following is taught by two Apple engineers. They have just now unequivocally said NOT to use the app delegate or global variables, and have said to use NSNotifications, delegates, and K-V observing.

If that is what the Apple engineers say, I am going to move in that direction.

The NSNotifications are pretty ingenious in that they don't really interfere with encapsulation that much. The listener only listens for the notification and an object - I don't think it has to know or care who sent it.

So in your example I would consider having the delegate post a notification that the label had changed, or better yet have the controller observe that property if possible.

Jake
I really have to watch the vids from Stanford.
Kriem
While NSNotifications work well for this case, I'd strongly advise against using too many of them in your app. Notifications are very difficult to debug, and you can't necessarily just look at your code to see when and where something is being fired.Just some words of warning.
Matt Ball
Hi Matt, I understand your concern about NSNotifications. In light of that, any other suggestions?
Jake
For a situation like this, where there's any reason why the two classes can't directly know about each other, posting and listening for NSNotifications is probably a good route. My intention wasn't to discourage their use (lord knows I use them plenty in my code), but to advise against their overuse.
Matt Ball