[Solved]: Trouble with design pattern
[Solved]: Trouble with design pattern
- Subject: [Solved]: Trouble with design pattern
- From: Carlos Eduardo Mello <email@hidden>
- Date: Sat, 19 Feb 2011 23:29:12 -0200
Thanks Andy, Jon, Kirk, Murat and everyone who responded.
All your advices were very usefull.
I decided to use the delegate pattern and everything is working
perfectly now. In fact I am starting to be able to implement sevall of
my planed funcionalities just by adding messages to my working
delegate protocol, and my views know nothing about my document. I am
tryng to make them more generic as well, so i can reuse them in other
projects with similar requirements.
I am also learning how to use Notifications, and will be using them
soon.
Thanks again.
On Feb 18, 2011, at 9:17 PM, aglee wrote:
I would use the delegate pattern. Look at some Cocoa classes that
have delegates to get the idea of the general pattern. Examples:
NSTableView, NSControl, NSURLConnection. My app, AppKiDo, provides
an easy way to list classes that have delegates, and to skim the
delegate methods. You could also search the Xcode doc window for
"delegate methods". I believe there is also documentation that
specifically discusses the pattern. And Erik Buck's book "Cocoa
Design Patterns" also discusses it in detail.
Here are the general steps to follow this pattern:
In MyView.h, declare a protocol something like this (typed in Mail,
untested):
@protocol MyViewDelegate
- (void)myView:(MyView *)view didMouseDown:(NSEvent *)mouseDownEvent;
@end
Have MyDocument conform to the MyViewDelegate protocol:
@protocol MyViewDelegate; // Forward declaration of the protocol.
@interface MyDocument : NSDocument <MyViewDelegate>
Add a delegate outlet to your custom view:
@interface MyView : NSView
{
//...
id <MyViewDelegate> myViewDelegate;
//...
}
// IMPORTANT:
// Declare the delegate property as assign, NOT retain, as delegates
are not retained in order to avoid retain loops.
@property (readwrite, assign) id <MyViewDelegate>myViewDelegate;
//...
@end
In IB, make the document the delegate of your custom view.
In MyView.m, send a myView:didMouseDown: message to the delegate at
whatever the appropriate time is. If you're overriding mouseDown:,
that's probably the right time, something like this:
- (void)mouseDown:(NSEvent *)event
{
[myViewDelegate myView:self didMouseDown:theEvent];
}
In MyDocument.m, implement the delegate method myView:didMouseDown:.
Note that MyView doesn't have to know that its delegate is a
MyDocument. It doesn't care. It just knows that the delegate is some
object that wants to be notified of mouse-down events.
Also note that delegate methods should take the object doing the
delegating as their first argument, and should use the name of the
delegating class (in this case "MyView", hence the "myView:" at the
beginning of the delegate method). You see this in all Cocoa
delegate protocols. This is in case you have two MyView instances
with the same delegate. The delegate can use the first argument to
tell which instance it is dealing with. Passing the delegating
object also allows the delegate method to get further information
about it if necessary.
An advantage of the delegate pattern over notifications is that
delegate methods can have return values. For example, you could have
myView:didMouseDown: return a BOOL indicating whether it did
anything with the event. If the superclass of MyView has a non-
trivial implementation of mouseDown:, you might want to fall back on
that behavior by changing the above method like this:
- (void)mouseDown:(NSEvent *)event
{
if (![myViewDelegate myView:self didMouseDown:theEvent])
{
[super mouseDown:event];
}
}
One last variation this pattern: you might want to make some
delegate methods @optional.
// In MyView.h:
@protocol MyViewDelegate
@optional
- (void)myView:(MyView *)view didMouseDown:(NSEvent *)mouseDownEvent;
@end
// In MyView.m:
- (void)mouseDown:(NSEvent *)event
{
if ([myViewDelegate
respondsToSelector:@selector(myView:didMouseDown:)
&& ![myViewDelegate myView:self didMouseDown:theEvent])
{
[super mouseDown:event];
}
}
I hope this is not too confusing.
--Andy
On Feb 18, 2011, at 02:20 PM, Carlos Eduardo Mello <email@hidden
> wrote:
Hi Everyone,
I am comming back to cocoa programming and to this list after a few
years without programming anything, so please forgive me in advance
for any stupid questions. I've read the docs, studied Hillegass's
book
again and searched the archives, but still couldn't find a definitive
answer to my questions.
My app is document-based, started from the XCode template. I have two
custom views which are outlets of MyDocument and some extra buttons
and text fields on the document's NIB file. Drawing, actions and
events work fine for the most part. but in order to do the fun stuff
with the app, I need to acess methods in the document as a result of
mouse events in the custom views, like updating the values in the
text
fields and making modifications to my data model classes (the data
engine is a collection of crossplatform C++ classes). I came across a
few suggestions in cocoa-dev's archives from a few years ago, but
they
didn't seem quite it.
1. Create outlets in the view's subclasses and point them to
MyDocument using IB;
2. Create outlets as in 1., but adding acessor methods to initialize
them;
3. accessing MyDocument with messages to the class hierarchy , as in
[[[self window] windowController] document];
I tried to use a mix of these approaches, or in other words: query
the
window controller for the document and then store its reference
inside
the views forq quicker access. Here's where I get stuck:
In order to use 1. or 2., I need to declare an instance of MyDocument
inside my view's classes. The compiler won't let me do this unless I
import MyDocument to the class definitions. The problem is that the
classes are already included in MyDocument and the "chicken/egg"
thing
makes XCode spit a zillion compile errors (naturally). I tried
declaring the variables inside the custom views as type 'id' and
tested the approach calling a MyDocument method which shows some data
in various textfields. The code compiled and ran, but MyDocument
never
got the messages.
// inside custom view's class definition
id document;
// inside awakeFromNib
document = [ [ [ self window ] windowController ] document ];
// inside mouseUp
[document refreshParameters];
If I just query the window controler for the document on demand like
this:
[ [ [ [ self window ] windowController ] document
refreshParameters ];
the code works, but I still can't get rid of those "message-not-
found"
warnings:
"warning: no '-refreshParamters' method found
(Messages without a matching method signature will be assumed to
return 'id' and accept '...' as arguments.)"
So my questions are:
1) Is there a better way to approach this?
2) Is it OK to just call the document like this and ignore the
compiler warning?
I'd really appreciate if someone could comment on this - i'd hate to
find out later that i'd been building on a bad design...
Thanks already for a y help.
Carlos.
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden