Register FAQ / Rules Forum Spy Search Today's Posts Mark Forums Read
Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old Mar 31, 2005, 04:06 AM   #1
Nutter
macrumors 6502
 
Join Date: Mar 2005
Location: London, England
Correct use of #import and @class (Objective-C)

Hello,

I'm a beginner Objective-C programmer, and I've just run into a problem when developing my first major project.

My program consists of several objects, arranged in a heirarchy (although not inheriting from one another).

Class A --> Class B --> Class C

Objects of Class A own several objects of Class B, which in turn own several objects of Class C.

However, Class B objects also contain a pointer to the Class A object that owns them, and Class C objects contain a pointer to the Class B object that owns them.

I had put the following lines in ClassA.h:
#import "ClassB.h"

ClassB.h:
#import "ClassA.h"
#import "ClassC.h"

and ClassC.h:
#import "ClassB.h"

However, Xcode was giving me parse errors for my definition of a ClassB pointer in ClassC.h.

I eventually realised that, because I'm chaining together #import statements and the compiler basically sticks it all together, ClassA.h actually contains ClassC, then ClassB, then ClassA, in that order, which of course means that ClassC is trying to reference ClassB before it has been defined.

The only way I could find to get around this was to add an "@class ClassB" line to ClassC.h, and a "@class ClassA" line to "ClassB.h", instead of the #import "ClassB.h" and #import "ClassA.h" statements.

My question (after such a long-winded run up) is whether this is the correct procedure? Am I missing something? Steve Kochan's Objective-C book doesn't mention this issue, so I'm not sure what I should do in the future. Surely if at some point my Class C objects needed to reference actual instance methods from Class B, rather than just defining pointers, the @class directive wouldn't be enough?

Any advice would be appreciated!

Thanks.
Nutter is offline   0 Reply With Quote
Old Mar 31, 2005, 04:34 AM   #2
caveman_uk
Guest
 
caveman_uk's Avatar
 
Join Date: Feb 2003
Location: Hitchin, Herts, UK
I'm a beginner too so this maybe completely wrong but could you use type 'id' for the parent pointer? Objective-C doesn't really care at compile time if the object responds to a message or not. It might give you a warning though. So my understanding is you can send a message to an id even if 'id' doesn't know about it at compile time.

Someone please correct me if I'm talking out of my bottom!
caveman_uk is offline   0 Reply With Quote
Old Mar 31, 2005, 04:48 AM   #3
robbieduncan
Moderator
 
robbieduncan's Avatar
 
Join Date: Jul 2002
Location: London
Reread what you said!

Do you need the circular pointers? If so do you need them to be strongly typed?

As mentioned above id will get round all this, although may generate compiler warnings (it will all be OK at runtime).

Otherwise the @class solution is acceptable.

If you use the id solution you can probably get round the compiler warnings by using protocol so your variable in Class B would look like id<ClassASelectors> owner; and you would have a ClassASelectors.h file that contains the selectors you want callable from Class B and Class A would be delcared something like @class ClassA <ClassASelectors>

Last edited by robbieduncan; Mar 31, 2005 at 04:53 AM.
robbieduncan is offline   0 Reply With Quote
Old Mar 31, 2005, 05:57 AM   #4
Nutter
Thread Starter
macrumors 6502
 
Join Date: Mar 2005
Location: London, England
Quote:
Originally Posted by robbieduncan
If you use the id solution you can probably get round the compiler warnings by using protocol so your variable in Class B would look like id<ClassASelectors> owner; and you would have a ClassASelectors.h file that contains the selectors you want callable from Class B and Class A would be delcared something like @class ClassA <ClassASelectors>
That went over my head!

Thanks for the id suggestion. I don't need these pointers to be statically typed, but I don't need them to be dynamically typed either, so isn't it better practice to statically type them?

What would happen if I wanted to call a ClassB method from ClassC.m? Would I need to add #import "ClassB" to ClassC.h?
Nutter is offline   0 Reply With Quote
Old Mar 31, 2005, 06:35 AM   #5
robbieduncan
Moderator
 
robbieduncan's Avatar
 
Join Date: Jul 2002
Location: London
Quote:
Originally Posted by Nutter
That went over my head!

Thanks for the id suggestion. I don't need these pointers to be statically typed, but I don't need them to be dynamically typed either, so isn't it better practice to statically type them?

What would happen if I wanted to call a ClassB method from ClassC.m? Would I need to add #import "ClassB" to ClassC.h?
Unless you are looking to call a Class method (this is confusing as all you classes are called ClassX!), i.e. one declared as + selectorName (note in Obj-C we should be talking about selectors, methods are Java terminology) you can only call against an instance of that class. You can call pass any message you like to any class in Obj-C, they may not respond to it though!

This is where you get your compiler warnings. The compiler will say something like "Class may not respond to selector ....". It will still compile and will work at run-time. What you are loosing is the compile time check that you have not made a spelling error. This is what my protocol solution was going to get around. I am at work right no, but in a few hours I can knock up some simple examples and post them up somewhere...
robbieduncan is offline   0 Reply With Quote
Old Mar 31, 2005, 06:55 AM   #6
caveman_uk
Guest
 
caveman_uk's Avatar
 
Join Date: Feb 2003
Location: Hitchin, Herts, UK
I'd be interested to see that as I had thought that you could probably do it with protocols but I didn't really know how you'd do it - I've not used them.
caveman_uk is offline   0 Reply With Quote
Old Mar 31, 2005, 07:05 AM   #7
caveman_uk
Guest
 
caveman_uk's Avatar
 
Join Date: Feb 2003
Location: Hitchin, Herts, UK
Quote:
Originally Posted by Nutter
Thanks for the id suggestion. I don't need these pointers to be statically typed, but I don't need them to be dynamically typed either, so isn't it better practice to statically type them?
I was always taught to give as strict typing as you could but in this case it's making life difficult for you when it needn't be. I came from a Pascal background so I didn't have much choice though.
Quote:
What would happen if I wanted to call a ClassB method from ClassC.m? Would I need to add #import "ClassB" to ClassC.h?
No. The runtime will try to send the message to the object and it's up to it then if it does anything. You can send a message to a 'nil' pointer and it's not an error. You can pass 'self' from the parent to the child class when it's initialized and assign the pointer in the child then. At that point the child can refer to the instance methods/selectors in the parent. At least that's what I think would happen.
caveman_uk is offline   0 Reply With Quote
Old Mar 31, 2005, 09:43 AM   #8
Nutter
Thread Starter
macrumors 6502
 
Join Date: Mar 2005
Location: London, England
Thanks for the replies, guys.

At the moment, using @class and statically typing works fine.

My classes aren't actually called ClassA, ClassB, and ClassC, I was just calling them that for the sake of generalisation!

I'm actually programming a Sudoku Solver. Heard of Sudoku?
http://www.timesonline.co.uk/sudoku
Nutter is offline   0 Reply With Quote
Old Mar 31, 2005, 11:45 AM   #9
robbieduncan
Moderator
 
robbieduncan's Avatar
 
Join Date: Jul 2002
Location: London
OK I'm home I've attached a really simple XCode project. It does nothing! It has ClassA and ClassB. ClassA contains an instance variable of type ClassB* and ClassB has an instance variable of type id (which would be pointed back at ClassA). It also has a protocol declaration showing how you can get the effect of static typing without the @Class declarations and a protocol instead.

Any questions?
Attached Files
File Type: zip UseProtocols.zip (16.8 KB, 170 views)
robbieduncan is offline   0 Reply With Quote
Old Apr 3, 2005, 05:04 AM   #10
Nutter
Thread Starter
macrumors 6502
 
Join Date: Mar 2005
Location: London, England
Thanks for that! Sorry it's taken me so long to reply.

So, using protocols like that allows you to use the id type, while still having the compiler check that the receiver can respond to the message.

My only question is: what is the benefit of doing it that way, rather than using the @class directive?

As a beginner, I find the categories and protocols a bit daunting. I understand what they can do, in theory, I'm just not sure when I should be using them. Obviously for simpler programs it's not necessary to bother using them, but I would like to get into good habits before it is too late...!
Nutter is offline   0 Reply With Quote
Old Apr 3, 2005, 05:12 AM   #11
robbieduncan
Moderator
 
robbieduncan's Avatar
 
Join Date: Jul 2002
Location: London
Quote:
Originally Posted by Nutter
Thanks for that! Sorry it's taken me so long to reply.

So, using protocols like that allows you to use the id type, while still having the compiler check that the receiver can respond to the message.

My only question is: what is the benefit of doing it that way, rather than using the @class directive?

As a beginner, I find the categories and protocols a bit daunting. I understand what they can do, in theory, I'm just not sure when I should be using them. Obviously for simpler programs it's not necessary to bother using them, but I would like to get into good habits before it is too late...!
No problem! I don't expect people to read code instantly

In this case there is no clear advantage in using a protocol over a class directive. I personally prefer the protocol method as it declares the messages you are going to use as opposed to the class declaration which, unless I am mistaken, simply tells the compiler that the class is declared elsewhere and not to complain!* Also by using a protocol you can replace the owner class (Class A) without needing to change the child class (Class B). As long as the new owner/container class implements the protocol this will be fine.

Protocols are really cool when you start dynamically loading code (think plugins) as you can ensure that the dynamically loaded class responds to some messages without knowing anything else about it.

*I just tried this. The compiler does still check that the messages called exist in the class (i.e. even though you don't import ClassA.h declaring ClassA via @class seems to allow the compiler to check that the messages exist when called from ClassB.m). So it looks like the @protocol thing is a little less useful/important than I thought. The reuse/changing owner class thing still stands.

Last edited by robbieduncan; Apr 3, 2005 at 05:23 AM. Reason: I was wrong :)
robbieduncan is offline   0 Reply With Quote
Old Apr 3, 2005, 05:25 AM   #12
Nutter
Thread Starter
macrumors 6502
 
Join Date: Mar 2005
Location: London, England
Ah, I see. Using @class doesn't let you statically type, it just lets you call the Class by name. So using a protocol is better.

Is this a very common situation to have, or is there a different way of designing object interactions that would avoid this problem?

I'm beginning to see how protocols can be useful. What about categories? Are they only useful for expanding a class without changing the source code?
Nutter is offline   0 Reply With Quote
Old Apr 3, 2005, 05:32 AM   #13
robbieduncan
Moderator
 
robbieduncan's Avatar
 
Join Date: Jul 2002
Location: London
Quote:
Originally Posted by Nutter
Ah, I see. Using @class doesn't let you statically type, it just lets you call the Class by name. So using a protocol is better.

Is this a very common situation to have, or is there a different way of designing object interactions that would avoid this problem?

I'm beginning to see how protocols can be useful. What about categories? Are they only useful for expanding a class without changing the source code?
See my correction above. It looks like you get full static typing via @class. Well as full as you get from Obj-C, you can pass messages that the class does not appear to respond to!

I don't encounter this very often and it tends to be slightly bad design. Normally you have one class that sends messages to others and gets responses but the target class does not normally initiate a message. Note that you should be using a MVC (model-view-controller) pattern. If you view ClassA as the controller and ClassB as part of the model then the only time that ClassB would want to send a message to ClassA is if the model data changes. Using bindings (or at least key-view observing) you can have the runtime do this work for you meaning you don't need the backwards pointer.

Catagories are only used to extend other classes. They can be useful even with your own classes as they allow you to split your class into a number of source files which can make managing your code easier. They are really useful for adding methods to API classes.
robbieduncan is offline   0 Reply With Quote
Old Apr 3, 2005, 06:29 AM   #14
Nutter
Thread Starter
macrumors 6502
 
Join Date: Mar 2005
Location: London, England
Are you sure? The compiler gives me messages along the lines of "ClassA may not respond to selector:". I think you were right originally when you said that using @class just tells the compiler to shut up and get on with it.

At the moment I'm working through Steve Kochan's Objective-C book, and am just starting to use the Foundation Framework. I'm not using bindings or any of advanced features of Cocoa.

When I designed my program, I intended the heirarchy to work in one direction only, as you describe. But I realised that the objects at the bottom of the heirarchy, having performed a certain change on their own state, would have to indicate this to the object at the top of the heirarchy. I thought it would be a good idea to establish a chain of messages going up the heirarchy, but perhaps I should change my design so that the objects can simply return this information to the object that called them.
Nutter is offline   0 Reply With Quote
Old Apr 3, 2005, 06:36 AM   #15
Nutter
Thread Starter
macrumors 6502
 
Join Date: Mar 2005
Location: London, England
Hmm, actually, I'm not sure if that will work.

The problem is that objects of ClassC are owned by more than one object of ClassB, which in turn is owned by more than one object of ClassA. When an object of ClassC records a certain change, I need all the objects above it in the heirarchy to record the change as well.

How can I do this except by sending a message to each of its owners? Is this something that Cocoa bindings would allow me to do (when I get to learning about that)?
Nutter is offline   0 Reply With Quote
Old Apr 3, 2005, 06:48 AM   #16
robbieduncan
Moderator
 
robbieduncan's Avatar
 
Join Date: Jul 2002
Location: London
I did a test with 2 new classes using @class instead of a protocol and got the expected "/Users/robbie/UseProtocols/ClassD.m:18: warning: `ClassC' may not respond to `-doSometing'". So it looks good to me.

In terms of the informing a group of other objects that the data has changed you have 2 options:

1) Before bindings (which is implemented via key-value observing) you would raise a notification and have all interested classes register for that notification in their init (or awakeFromNib if appropriate) methods.

2) Use key-value observing or bindings and get the runtime to automatically tell your objects when the value is changed. This requires your class to be fully key-value compliant. There are docs in this.

For the moment I would go with notifications as they are easy to set up and understand.
robbieduncan is offline   0 Reply With Quote
Old Apr 3, 2005, 07:45 AM   #17
Nutter
Thread Starter
macrumors 6502
 
Join Date: Mar 2005
Location: London, England
Are notifications part of the Foundation Framework of the AppKit?

I'll look into this. Thanks for the tip.
Nutter is offline   0 Reply With Quote
Old Apr 3, 2005, 08:04 AM   #18
robbieduncan
Moderator
 
robbieduncan's Avatar
 
Join Date: Jul 2002
Location: London
Quote:
Originally Posted by Nutter
Are notifications part of the Foundation Framework of the AppKit?

I'll look into this. Thanks for the tip.
Foundation and the AppKit are different things. The AppKit builds on Foundation for GUI apps. Look at NSNotification (which is in Foundation).

To register for a notification you do something like:

Code:
	// Register for notification.
	[[NSNotificationCenter defaultCenter] 
		addObserver:self 
		   selector:@selector(deleteMessage:) 
			   name:SMS_DELETE_MESSAGE_NOTIFICATION_NAME
			 object:nil];
deleteMessage: is the handler for the notification. It has a signature something like:

Code:
- (void) deleteMessage:(NSNotification *)aNotification
{
NSDictionary *userInfo = [notification userInfo];
...
}
The notification is raised via something like:

Code:
	NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
	[userInfo setObject:path 
				 forKey:SMS_DELETE_MESSAGE_PATH_KEY];
	NSNotification *deleteNotification = [NSNotification notificationWithName:SMS_DELETE_MESSAGE_NOTIFICATION_NAME 
																	   object:self 
																	 userInfo:userInfo];
	[[NSNotificationCenter defaultCenter] postNotification:deleteNotification];
You can use the userInfo dictionary to pass any amount of data between the poster and the receiver(s). Obviously anything in all caps above is a #define constant. I tend to put these in a separate .h file to ensure they are the same everywhere and that I don't make typing errors.

Last edited by robbieduncan; Apr 3, 2005 at 08:07 AM.
robbieduncan is offline   0 Reply With Quote
Old Apr 4, 2005, 09:39 PM   #19
GeeYouEye
macrumors 68000
 
GeeYouEye's Avatar
 
Join Date: Dec 2001
Location: State of Denial
Send a message via AIM to GeeYouEye Send a message via Yahoo to GeeYouEye
I think the problem is being made bigger than it actually is; the general thing to do is use @class in the .h files and use #import in the .m files.

So, if I'm understanding this right, the question is based on the following

@interface ClassA : NSObject
{
ClassB *instance1;
ClassB *instance2;
ClassB *instance3;
}
//do stuff
@end

@interface ClassB : NSObject
{
ClassA *owner;
ClassC *instance1;
ClassC *instance2;
ClassC *instance3;
}
//do stuff
@end

@interface ClassC : NSObject
{
ClassB *owner;
}
//do stuff
@end


The solution, and the correct one, is, yes, use the @class directives in the .h files, and the #imports in the .m files.

@class ClassB; in ClassA.h
@class ClassA; and @class ClassC; in ClassB.h
@class ClassB; in ClassC.h

#import "ClassB.h" in ClassA.m
#import "ClassA.h" and #import "ClassC.h" in ClassB.m
#import "ClassB.h" in ClassC.m

Hope that answers your question.
__________________
I bring order to chaos. You are in chaos Windows, you are the contradiction, a bug wishing to be an OS.
Visit Softyards Software
NEW DEFINITION OF GEEK
Like politics, free speech, computers, entertainment, and more? Join us at Wordforge.net
GeeYouEye is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Similar Threads
thread Thread Starter Forum Replies Last Post
The Java class file "xxx.class" could not be launched. piratemacfan Mac Applications and Mac App Store 1 Aug 31, 2013 03:24 PM
Referencing a child class in a parent class, good or bad? SAIRUS iPhone/iPad Programming 11 Aug 28, 2013 11:59 AM
new objective C-class won't create .h file OriginalJef iPhone/iPad Programming 2 Mar 3, 2013 03:24 PM
Resolved: iOS, Objective-C Declaring Variable to entire Class. resetme iPhone/iPad Programming 3 Dec 17, 2012 03:40 PM
Difference between Public class and private class. zijianz iPhone/iPad Programming 4 Jun 29, 2012 09:29 PM

Forum Jump

All times are GMT -5. The time now is 09:32 AM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC