On 22 May 2008, at 3:53 pm, Johnny Lundy wrote:

Oh, I have no problem invoking API methods that are built-in to Cocoa.

It's dealing with two of my own classes that I never understood.

OK, well alright, here's the secret: there is no difference!

I was referring to something like the following trivial setup:

Two custom classes in my project, MyClassA and MyClassB, each with its .h and .m files.

Assume MyClassB has an instance method "- (id) solve:aFoo".

I want to send the "solve:" message to an instance of MyClassB from MyClassA.

Now IF MyClassB were instantiated, and IF its instance were myClassBInstance, I could write

id myBar = [myClassBInstance solve:myFoo];

From one of my instance methods in MyClassA's instance. (Which would have been somehow instantiated).

The problem: how to instantiate MyClassB so that it actually HAS a "solve:" method to send a message to.

I did this once in a project just to see if it would work: from inside an instance method of MyClassA:

MyClassB *myClassBInstance = [[MyClassB alloc] init];
id myBar = [myClassBInstance solve:myFoo];

And it worked, but it seemed like such a kludge that I just incorporated both classes together.

That's exactly right. Not sure why you think it's a kludge - possibly because in a trivial example case like this it seems pointless to have two separate objects when one will do perfectly. That doesn't mean your example is wrong, it just means the example doesn't give much insight as to *why* you'd do this. The example illustrates only the *how* and does so perfectly!

The advantage comes when your designs get more complex. Objects give you a clean separation of responsibilities. Why doesn't NSString deal with bezier paths? After all, it *could*, but it would be a crazy design, since sometimes you want one and not the other, and they don't seem related.

Deciding where to break responsibilities down into separate classes is an art, I have not seen too many text books going into that too much. The M-V-C paradigm is a shorthand guide to one useful approach to where certain responsibilities lie, but believe me, there are many fuzzy edges in between.

Besides, if you don't have IB to instantiate a class, how can you do it? If there is no instance of the class, where would you invoke the alloc/init messages from? From main()?

Well, it depends. But never main() - I never touch main in a Cocoa project.

I usually instantiate objects on a "need to use" basis. So if my class always needs certain objects, I might instantiate them in my init method (and thus get rid of them in my dealloc method). Or at any other time that I need something that another object can do for me.

Example, I have a custom view class. In my -drawRect method I'd like to draw a solid red box somewhat inside my bounds.

- (void)        drawRect:(NSRect) updateRect
{
// I want to draw a box inside my bounds. So get the bounds and inset it:

        NSRect myRect = NSInsetRect([self bounds], 10, 10 );

// I'd like it drawn in red. I can use the NSColor class to set that up:

NSColor* red = [NSColor redColor]; // get an instance of NSColor I'm calling "red"...
        [red set];                              // ...and make it the current 
drawing colour

// Hmm, I can't be bothered to work out how to fill the rect pixel by pixel in video memory, but I know another object that can do it
        // for me, so let's just make one and give him the job:

NSBezierPath* myPath = [NSBezierPath bezierPathWithRect:myRect]; // get a new bezier path instance based on my rect...
        [myPath fill];                                                          
// ...and make it fill itself
        
        // OK, job done - and I barely got my hands dirty ;-)
}

Note that in this case the objects are instantiated by their class convenience methods, and autoreleased. But it would be equally valid to use alloc + init and then release them directly when you're done with them. Here we are using Cocoa's own objects, but if you had a class you made called 'MyColouredRectFiller' for example, you could make one of those, hand the job to it then throw it away afterwards. Why bother, when you can just write the code right here? Well, suppose you have another completely unrelated place where you want to draw a coloured rect. You can use a MyColouredRectFiller there too which you've already written and debugged for this case. So just use it. That's the real benefit of objects - reusing code that you already wrote and debugged once, so you can save time by not having to do it again. When objects get more complex this time saving is very, very significant. And when whole projects get more complex, it's about the only way that makes it even remotely manageable.

You might argue that this is just the same as having a 'fill rect' procedure that you can call from two places, and so it is. But once again, it's the simplicity of the example that is misleading. Instead of an object to fill a rect (there isn't one, so that tells you it's probably too simple to be worth it) imagine you want to edit some text, word-processor stylie. Your client code just wants to get some text from the user, it doesn't care how, but you don't want your app to be hideously unusable either. So you can use an NSTextView object to handle that (very complicated) job in the standard way. You couldn't really do that with a simple procedure call (old timers will not remember Apple's procedural text editor, TextEdit, with much fondness I imagine). Objects are useful for encapsulating a unit of functionality in a way that can be easily reused in many similar situations, but which have no other dependencies (if well written).

And if you DO have IB, then is the usual technique to instantiate one class in IB, and then code the instantiations of other classes in the first one, as I did above? Or create one IB generic object for each class and give it an IBOutlet so it has a name?


My view is that Interface Builder should *only* be used to set up interfaces, and no more. It can actually go further and set up a lot more, but I think it's a mistake to get too carried away with instantiating objects in IB just because you can. Apart from anything else it doesn't help you learn how to do it manually, which for a project of any substance you *must* know. I typically go as far as my first level controller in IB (so that I've got *something* that has all of those IBOutlets to wire up to controls) but everything else is done in code. So IB instantiates the controller, but all of its actual "guts" you write yourself (I'm deliberately leaving bindings out of this).

On the other hand you could create everything in code - interface included, but that's very tedious as well - after all, the positioning and sizing of interface items is more to do with aesthetics than for any technical reason, so IB is great for allowing you to do things "by eye" that would be a real chore in code. The point is that given a project, you have tools optimised for some parts of the job but not all of it, but there's some overlap. You need to learn when to use one and not the other.

This is not in the docs, by the way. I think they just assume that you know it.

I'm not sure that's true. The docs do tell you what IB is good for, but they do not tell you in great detail how it may also be abused in all the ways it could be. I think you'll find that's true of almost any product or object in the real world that needs any explanation. (Though that's changing - e.g. "petrol fumes may be harmful if inhaled" well, whodathunkit?)


I've always avoided it by using just one class. I never understood the need for more than one class anyway.

For a very small project or simply learning the ropes that might be all you need, but most projects of any size will have many classes - possibly thousands. Clearly, the existence of all these separate classes should tell you that people do think they are a good idea! You might not "get it" yet, but when you do, it will be a real "of course!!!" moment. Let's hope it's soon ;-)

cheers, G.




_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to