Want to highlight a helpful answer? Upvote!

Did someone help you, or did an answer or User Tip resolve your issue? Upvote by selecting the upvote arrow. Your feedback helps others! Learn more about when to upvote >

Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Objective-C class design, inheritance & delegation

Hi,

I'm an experienced professional C & C++ coder, and I'm looking at writing my first Objective-C app to learn Cocoa. Now my design calls for a base "Device" controller class, which has functionality to add & remove Devices, connect & disconnect them and so forth. But I want to actually have different types of Devices, each of which has a different Process function. So for example, I can create a "DistortionDevice" class and connect it to the "FilterDevice" class.

In C++, I would do this by having a class Device, with common methods & data -- like Device::connect(), Device::remove(), Device::isRunning for example, and some more pure virtual methods like Device::getName()=0, Device::process()=0 and maybe even some protected methods like Device::canConnect()=0. I could then create a number of subclasses of Device which then provide concrete implementations of these pure virtual methods. And my host program just deals with Devices that are added to it, etc.

Now, I'm not sure how to do this 'correctly' in Objective-C. I could have a class Device with all the common methods & data as before, so that handles connection, removal, activation of devices and so on. But there doesn't seem to be any way of supplying pure virtual methods like -(NSString*)getName=0; and -(void)process=0; Also, as all methods declared in the interface are public, there is no way to have a protected pure virtual methods like -(BOOL)canConnect=0;

The way that Objective-C seems to work is that you use @protocols to define pure virtual methods. But, if I understand correctly, a protocol cannot have any instance data or methods of it's own. So I would need to reimplement the common methods like -(void)connect; in every single class that conforms to this protocol. And that seems wrong to me as well.

So I'm guessing that cocoa would want something like: A DeviceProcessing protocol that defines the pure virtual functions that I want, like -(NSString*)getName; and -(void)process; and a base Device class that has the common functionality & data like -(void)connect; and so on. My "concrete" devices are then derived from the Device base class AND conform to the DeviceProcessing protocol.

Is that right? What would be the best way of linking all this up? Should the Device base class implement the DeviceProcessing protocol, and implement this by forwarding calls through to it's delegate, or what?

iMac 2.8GHz 24", Mac OS X (10.5.4)

Posted on Sep 16, 2008 5:13 AM

Reply
11 replies

Sep 16, 2008 6:19 AM in response to wmerrifield

wmerrifield wrote:
I'm an experienced professional C & C++ coder


Me too. But I can't ever see myself going back to C++. I spent 15 year learning it and only realized too late that it was a waste of time.

In C++, I would do this by having ... pure virtual methods


Now, I'm not sure how to do this 'correctly' in Objective-C ... there doesn't seem to be any way of supplying pure virtual methods


The way that Objective-C seems to work is that you use @protocols to define pure virtual methods.


I think you are still stuck in C++ thinking. C++ is just a big hack. It isn't a pure Object-Oriented language like Objective-C. A "protocol" is a contract that a polymorphic instance must fulfill. Pure virtual methods are a C++ hack to try to accomplish this.

But, if I understand correctly, a protocol cannot have any instance data or methods of it's own. So I would need to reimplement the common methods like -(void)connect; in every single class that conforms to this protocol. And that seems wrong to me as well.


You can still have base classes. Define a base class that implements the common methods. You can either have a base protocol to use with the base class or just have your derived classes conform to the protocol.

So I'm guessing that cocoa would want something like: A DeviceProcessing protocol that defines the pure virtual functions that I want, like -(NSString*)getName; and -(void)process; and a base Device class that has the common functionality & data like -(void)connect; and so on. My "concrete" devices are then derived from the Device base class AND conform to the DeviceProcessing protocol.

Is that right?


Sounds like it.

What would be the best way of linking all this up? Should the Device base class implement the DeviceProcessing protocol, and implement this by forwarding calls through to it's delegate, or what?


That is a harder question to answer. First of all, do you need all the protocols with your Device classes? Are these device classes used as delegates, via an "id<DeviceProcessingProtocol>" pointer? Do you need to have all this figured out in your first Objective-C app?

I think it is more important to forget all the C++ hacks and learn how Objective-C does things. Also, use Objective-C++ instead. That way, you get a few nice little details from C++ that C lacks - the best of both worlds. For what you are talking about, you can go into as much detail as you want with base classes, protocols, etc - but it is only because you want to. In most cases, it will work just fine without that much detail too. In C++, you must have the virtual methods and must use a base class pointer for polymorphic behavior. In Objective-C, you can just use an "id" and #import your class and it will figure it out on its own. All you have to do is pick which level of detail along that OO continuum that you are happy with.

Sep 16, 2008 7:02 AM in response to etresoft

{quote:title=etresoft wrote:}
I think you are still stuck in C++ thinking.
{quote}
Quite possibly, but that's all I know, and I'm trying to get my head around how Objective-C works. I'm getting to grips with things, and then, what I think is a quite simple issue (I have a host that interfaces to a bunch of Devices; each Device has some common functionality, and some specialised processing) and my usual approaches do nothing here.

{quote:title=etresoft wrote:}I think it is more important to forget all the C++ hacks and learn how Objective-C does things.
{quote}
Well, yes, but after getting my head around the syntax, I'm struggling with what I thought would be a simple architectural issue. I want to do things the appropriate way for Objective-C, but...
{quote:title=etresoft wrote:}
That is a harder question to answer. First of all, do you need all the protocols with your Device classes? Are these device classes used as delegates, via an "id<DeviceProcessingProtocol>" pointer? Do you need to have all this figured out in your first Objective-C app?
{quote}
...doesn't mean anything to me. I think a simple concrete answer would help me a lot 🙂

"First of all, do you need all the protocols with your Device classes?"
I don't understand the question 🙂

"Are these device classes used as delegates, via an id<DeviceProcessingProtocol> pointer?"
I don't understand this question either 🙂

"Do you need to have all this figured out in your first Objective-C app?"
It's my first significant app 🙂 I have modified example code, but they don't do what I want 🙂

What I do want -- I have a Host class which manages a bunch of Devices of different types (some DistortionDevices, some FilterDevices, etc.) Now all Devices have a bunch of common functionality (turn on & off, get connected to other Devices, etc.) and the Host will also want to Process each of the Devices it holds, and each subtype of Device will need to do something different (so the DistortionDevice will do different processing from a FilterDevice, etc.)

What classes, protocols and so on will I need, and how will they interact?

{quote:title=etresoft wrote:}
Also, use Objective-C++ instead.
{quote}
I'd rather not; I'm not a big fan of C++, despite it paying the bills.

Sep 16, 2008 8:13 AM in response to wmerrifield

If I understand the question correctly, you are asking, "How do I trigger the methods in the root controller class from other objects (e.g. in the view)?"
The answer is nil-targeted actions, and this page has a nice description of the process:
http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ch apter2_section_3.html#//appleref/doc/uid/TP30001163-CH11-SW5

For example, a button in a view class wants to target a method called toggleView: in the controller class.
Provided the following lines are in the view class that displays the button:

[button setTarget:nil];
[button setAction:@selector(toggleView:)];

the Objective-C runtime will successfully find the toggleView: method in the controller class as long as the controller class is part of the responder chain (i.e, it inherits from NSResponder, or it is the main window's delegate, etc.)
This is on the responder chain:
http://developer.apple.com/documentation/Cocoa/Conceptual/EventOverview/EventArc hitecture/chapter2_section6.html

Here is another example from the sample code project called Metronome.

// Add 'i' button
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
infoButton.frame = CGRectMake(256, 432, 44, 44);
[infoButton addTarget:metronomeViewController action:@selector(toggleView:) forControlEvents:UIControlEventTouchUpInside];

where, again, toggleView is in a root controller class.
Note that the selector data type allows you to pass method names as parameters.

Sep 16, 2008 9:01 AM in response to johncmurphy

Thanks, but I don't think I'm making myself clear.

At the moment, it's more of an architecture issue, rather than anything like "How do I trigger the methods in the root controller class from other objects?" I'm aware of selectors and message passing to nil and so on (and it seems pretty nifty).

What I want to know is what is the correct/best Objective-C way of reproducing this kind of thing (this is a sketch of a C++ implementation to try and get my point across, rather than any kind of working code, hence the roughness):

/ ******************************************************************/
class Device
{
public:
virtual char* getName() = 0;

void render(void *data);

void enable(bool running) { mIsRunning = running; }
void setParam(int newValue) { mParamValue = newValue; }
Device *pDevice appendDevice(Device *pDevice) { mpNextDevice = pDevice; return pDevice; }

protected:
virtual void process(void *data) = 0;
int mParamValue;

private:
bool mIsRunning;
Device *mpNextDevice;
};

void Device::render(void *data)
{
if(mIsRunning)
{
printf("Applying %s effect...\n", getName());
process(data);
}
if(mpNextDevice)
{
mpNextDevice->render(data);
}
}

/ ******************************************************************/
class DistortionDevice
{
public:
virtual char* getName() { return "Distortion"; }

protected:
virtual void process(void *data);
};

void DistortionDevice::process(void *data)
{
// Run a distortion algorithm on "data" based on the parameter value
DoDistortion(data, mParamValue);
}

/ ******************************************************************/
class FilterDevice
{
public:
virtual char* getName() { return "Filter"; }

protected:
virtual void process(void *data);
};

void FilterDevice::process(void *data)
{
// Run a filter algorithm on "data" based on the parameter value
DoFilter(data, mParamValue);
}

/ ******************************************************************/

And then I want to be able to do this kind of thing in my DeviceHost:

void test()
{
Device *pFirstDevice = new DistortionDevice();
pFirstDevice->setParam(50);

Device *pSecondDevice = new FilterDevice();
pSecondDevice->setParam(10);

Device *pThirdDevice = new DistortionDevice();
pThirdDevice->setParam(90);

pFirstDevice->appendDevice(pSecondDevice)->appendDevice(pThirdDevice);

pFirstDevice->render(&myDataBuffer);
}

And have the contents of the myDataBuffer being distorted (1st device), filtered (2nd device), and distorted again (3rd device). This C++ architecture also allows me to add more units easily by subclassing Device and providing an appropriate implementation of process() and getName().

Sep 16, 2008 9:20 AM in response to wmerrifield

What I've got at the moment is something like this...

Is this correct, stupid, wrong-headed, or the right thing to do?

EDIT: My square brackets have gone; I don't know how to get them back!

/ ******************************************************************/
@protocol DeviceProcessing
-(NSString*)getName;
-(void)process:(void*)data;
@end

/ ******************************************************************/
@interface Device : NSObject
{
BOOL mIsRunning;
int paramValue;
Device* nextDevice;
id<DeviceProcessing> processor;
}

-(id)initWithProcessor:(id<DeviceProcessing>)obj;

-(void)enable:(BOOL)running;
-(void)setParamValueTo:(int)newValue;
-(Device )appendDevice:(Device)nextDevice;

-(NSString*)getName;
-(void)render:(void*)data;

@end

/ ******************************************************************/
@implementation Device

-(id)initWithProcessor:(id<DeviceProcessing>)obj
{
self = [super init];
if(self)
{
processor = obj;
}
return self;
}

-(NSString*)getName
{
return [processor getName];
}

-(void)render:(void*)data
{
if(mIsRunning)
{
[processor process:data];
}
if(nextDevice)
{
[nextDevice render:data];
}
}

-(void)enable:(BOOL)running
{
mIsRunning = running;
}

// etc...

@end

/ ******************************************************************/
@interface DistortionDevice : Device <DeviceProcessing>
{
// Any Distortion-specific data
}

-(NSString*)getName;
-(void)process:(void*)data;

@end

/ ******************************************************************/
@implementation DistortionDevice

-(NSString*)getName
{
return @"Distortion";
}

-(void)process:(void*)data
{
// Run a distortion algorithm on "data" based on the parameter value
DoDistortion(data, paramValue);
}

@end

/ ******************************************************************/
@interface FilterDevice : Device <DeviceProcessing>
{
// Any Filter-specific data
}

-(NSString*)getName;
-(void)process:(void*)data;

@end

/ ******************************************************************/
@implementation FilterDevice

-(NSString*)getName
{
return @"Filter";
}

-(void)process:(void*)data
{
// Run a filter algorithm on "data" based on the parameter value
DoFilter(data, paramValue);
}

@end

/ ******************************************************************/

And then code like this in DeviceHost:

void test()
{
Device* firstDevice = [DistortionDevice alloc]
[firstDevice initWithProcessor:firstDevice];
[firstDevice setParamValueTo:50];

Device* secondDevice = [FilterDevice alloc];
[secondDevice initWithProcessor:secondDevice];
[secondDevice setParamValueTo:10];

Device* thirdDevice = [DistortionDevice alloc];
[thirdDevice initWithProcessor:thirdDevice];
[thirdDevice setParamValueTo:90];

[[firstDevice appendDevice:secondDevice] appendDevice:thirdDevice];

[firstDevice render:&myDataBuffer];
}

But this bit:

Device* firstDevice = [DistortionDevice alloc]
[firstDevice initWithProcessor:firstDevice];

seems really ugly and could well be broken... Is this how you have to do it, or is there another way?

Message was edited by: wmerrifield -- why are my brackets being strange?

Sep 16, 2008 9:19 AM in response to wmerrifield

Don't worry about things like virtual functions and such. Protocols are really even optional as well, they are just convenient and prevent warnings at compile time. Remember that Objective-C is weakly typed and dynamic. Methods are bound at runtime, not at compile time.

What that means as far as class implementation is pretty huge. if you have a method -process that you want to call on a bunch of objects, those objects don't have to be related in any way except that they all implement that method. So, things like protocols and superclasses are not always strictly even necessary. They do help you catch things at compile time, but you'll notice that you only get warnings, and not errors, since you could always supply an implementation at runtime.

In your case I think I'd implement a base Device class in the way you would normally do. Then each subclass can implement the methods it needs to override. If you want to call the parent class functionality in the subclass then:

-(void)process {
[super process];
// Perform subclass specific stuff
}


The subclass has access to the instance variables of the superclass as you would expect. Now, you could define a protocol with the methods that the subclass should implement, or you could just put empty methods in the superclass that do nothing, and if you want to be paranoid have them raise an exception if they are called. Another alternative is just for the subclasses to implement the methods that you need and don't bother with a protocol or empty methods. That's OK, and it prevents you from having to write an entire protocol and clutter up your superclass with a lot of junk. The downside is that it forces you to remember to implement all the methods and you won't find out that you missed one until runtime. That's not all bad, though. You do tend to find errors at runtime more often in Objective-C but that's just the nature of the beast if you are using the dynamic nature to its fullest.

Sep 16, 2008 9:29 AM in response to wmerrifield

wmerrifield wrote:
"First of all, do you need all the protocols with your Device classes?"
I don't understand the question 🙂


Protocols are "formal". You don't need to use them for anything. Objective-C also has the concept of "informal" protocols. Basically, if you have an "id", you can send any message you want to it and it won't crash. The worst that will happen is that you'll get an error message saying the object doesn't respond to that selector. If your objects respond to the messages you are sending them, you don't need formal protocols - or base classes either, for that matter.

"Are these device classes used as delegates, via an id<DeviceProcessingProtocol> pointer?"
I don't understand this question either 🙂


Usually, delegates are just specified as type of "id". What you are then supposed to do is, before sending it a message, see if it responds to the selector you want to send. If it does, call it. If not, no big deal, it is probably just an informal protocol, silently ignore it. When you use the above construct, you are saying that an id "conforms to" a given formal protocol. Once this is true, you don't need to check to see if it responds to the selector because the compiler is guaranteeing that it will.

Also, an more importantly, protocols aren't required and, while not uncommon, are not used all the time. For what you are describing, most people wouldn't use protocols.

"Do you need to have all this figured out in your first Objective-C app?"
It's my first significant app 🙂 I have modified example code, but they don't do what I want 🙂


You can just write the code using base class pointers and ignore protocols for now. That will be much closer to your current C++ experience.

What I do want -- I have a Host class which manages a bunch of Devices of different types (some DistortionDevices, some FilterDevices, etc.) Now all Devices have a bunch of common functionality (turn on & off, get connected to other Devices, etc.) and the Host will also want to Process each of the Devices it holds, and each subtype of Device will need to do something different (so the DistortionDevice will do different processing from a FilterDevice, etc.)

What classes, protocols and so on will I need, and how will they interact?


I guess you would need a base class called "Device". Derived classes would be "DistortionDevice" and "FilterDevice". Write the code in standard OO-fashion. Assume every object is some kind of "Device *" and they all have some common data and common methods. You restrict yourself to those common elements and it all just works. If you have some device that is radically different from other devices and you need to call a method that isn't in Device, you are still in standard OO-land. Either down-cast the pointer (not-recommended but not necessarily unsafe or unusual) or improve your architecture.

Objective-C will issue a warning if it thinks you are doing something wrong.

Delegates are a way to have one class hierarchy talk to another. If you want to have your Devices update the display, they may have a "controller" delegate that they can notify when they change. When you do something like

[[self delegate] notifyUpdate: self]

your Device class needs to know about the "notifyUpdate" method somehow. You can #import the actual controller class, but that may give you a dependency you don't need or want. You can, however, justify a dependency on some "DeviceResponder" protocol. Then, if your delegate conforms to that protocol "id<DeviceResponder>", you can call the notifyUpdate method on the controller without having to know too much about the controller.

{quote:title=etresoft wrote:}
Also, use Objective-C++ instead.
{quote}
I'd rather not; I'm not a big fan of C++, despite it paying the bills.


There are some things in C++ that are handy, such as declaring variables where you want them. Don't try something like

for(int i = 0; i < 10; ++i)

in Objective-C. You don't have to use templates and C++ classes unless you want to, and you probably wouldn't.

Sep 16, 2008 9:32 AM in response to wmerrifield

Off the top of my head, I would think the way to set up your project would be as follows.
A Device object that performs all of the operations on the data.
Device.h

#import <Cocoa/Cocoa.h>
@interface Device : NSObject {
//instance variable declarations
}
-(void)distort:(id)anArgument;
-(void)filter:(id)anArgument;
-(void)somethingElse:(id)anArgument;
@end

Device.m

#import "Device.h"
@implementation Device
-(id)init {
//constructor code
}
-(void)distort:(id)anArgument {
//do something
}
//etc.
@end


and a controller class that instantiates a Device object, obtains some data from somewhere, and runs the data through the Device.
AppController.h

#import <Cocoa/Cocoa.h>
#import "Device.h"
@interface AppController : NSObject {
Device *device;
}
//method declarations
@end

AppController.m

#import "AppController.h"
@implementation AppController
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
device = [Device alloc] init];
id *data = //put something here
[device filter:data];
[device distort:data];
}
@end


After the operations are performed, the data would be sitting out there somewhere in its modified state.

Sep 16, 2008 10:41 AM in response to wmerrifield

Sorry, when I made that previous post I didn't see your subsequent post. Now I think I know what you're doing-something like Bias Peak, right? You have a stream of audio data, to which you want to apply effects in sequence.
So, what I posted before will not work, but the idea is the same.
The project would consist of a bunch of individual classes for all of the possible devices. The app controller would add instantiate the devices that are needed and put them in an array, which would be applied to the data in sequence.
The problem is, of course, what to do with all of the shared device methods?
It's funny, while the Cocoa framework relies heavily on subclassing, I always try to avoid it when creating my own custom classes. It just seems to cause a lot of headaches.
So the options are a protocol, or a separate class (or classes) for the common methods. The decision would probably come down to how to most efficiently access those common methods. Nil-targeted actions are nice, but require a little more processing. At the same time, if the class that contains the common methods is too large, you wouldn't want each device to create a separate instance of it.

Objective-C class design, inheritance & delegation

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.