Reputation: 15227
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?
Upvotes: 7
Views: 2896
Reputation: 15227
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.
Upvotes: 4
Reputation: 57188
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.
Upvotes: 2
Reputation: 8705
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.
Upvotes: 0
Reputation: 6628
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.
Upvotes: -1