views:

95

answers:

5

I have 3 classes of objects. All 3 classes share some properties in common, as color, text, etc.

For example, I can have this

Class1 *objectA = [[Class1 alloc] init];
objectA.myColor = [UIColor redColor];

Class2 *objectB = [[Class2 alloc] init];
objectA.myColor = [UIColor redColor];

Class3 *objectC = [[Class3 alloc] init];
objectA.myColor = [UIColor redColor];

... etc.

Now I need, for example, to create a method that can change the color of a given object, whatever class it represents.

A typical method would be

- (void) changeColor:(Class1*) myOBJ toColor:(UIColor*)myColor {
   myOBJ.color = myColor;
}

when in fact I need this

- (void) changeColor:(???) myOBJ toColor:(UIColor*)myColor {
   myOBJ.color = myColor;
}
// what to put on ??? to make it generic? Is this a "whatever" kind?

thanks


EDIT

the problem of using this approach

- (void) changeColor:(id)myOBJ toColor:(UIColor*)myColor {
   if ([myOBJ respondsToSelector:@selector(setColor:)]) {
       myOBJ.color = myColor;
   }
}

is this. Imagine I want to set the frame of the object. Then I will have to have this:

- (void) changeColor:(id)myOBJ newFrame:(CGRect)myFrame {

 if ([umID isKindOfClass:[Class1 class]]) {
   Class1 *oneObj = (Class1 *)myObj;
   oneObj.frame = myFrame;
 }

 if ([umID isKindOfClass:[Class2 class]]) 
   Class2 *oneObj = (Class2 *)myObj;
   oneObj.frame = myFrame;
 }


 if ([umID isKindOfClass:[Class3 class]]) 
   Class3 *oneObj = (Class3 *)myObj;
   oneObj.frame = myFrame;
 }

}

in other words, I will have to repeat the same stuff 3 times... right?

in other words, the problem is not solved as this is the same of having 3 methods, one for each class.

A: 

??? will be 'id'.

Graham Perks
I have edited the question. Please read it.
Digital Robot
+3  A: 

You have a couple options. The simplest, and "most dangerous" approach is to use a type id. This will let you pass in any object, but you'll want to test that it actually has a color property before you try and set it.

- (void) changeColor:(id)myOBJ toColor:(UIColor*)myColor {
   if ([myOBJ respondsToSelector:@selector(setColor:)]) {
       myOBJ.color = myColor;
   }
}

(That said, with the responds to selector check, this approach isn't all that dangerous, and it's much more flexible than the next idea.)

Another approach is to have all your objects inherit from a shared base class that has a color property. Then your parameter type would be the base class. This approach could be considered "safer" as the compiler would check that you're passing in the correct type of object. This approach also requires considerably more code.

If you want to use the first approach, but set something other than color, adjust the respondsToSelector: call appropriately.

- (void) changeFrame:(id)myOBJ newFrame:(CGRect)myFrame {
   if ([myOBJ respondsToSelector:@selector(setFrame:)]) {
       myOBJ.frame = myFrame;
   }
}

In general, if you want to know if an object supports propertyX, use [myOBJ respondsToSelector:@selector(setPropertyX:)]. If the passed in object is declared as id, you can then call [myOBJ setPropertyX:newPropertyValue] or myObj.propertyX = newPropertyValue.

Robot K
I have edited the question. Please read it.
Digital Robot
I've updated the example.
Robot K
+4  A: 

Maybe you can use protocols? Make Class1, Class2 and Class3 conform to a protocol with a property myColor. Then you could have a method like this (assuming your classes are of type UIView and your protocol is called ColorProtocol):

- (void) changeColor:(UIView<ColorProtocol>*) myOBJ toColor:(UIColor*)myColor {
   myOBJ.color = myColor;
   myOBJ.frame = ...;
}

Here is what your protocol definition could look like:

@protocol ColorProtocol

@property (nonatomic, retain) UIColor *myColor;

@end

Change your class definition files (.h) as follows to specify that you will conform to the protocol:

interface Class1 : UIView <ColorProtocol> {...}

In the implementation files (.m) you must simply synthesize the myColor property to conform to the ColorProtocol:

@synthesize myColor;

If your classes are very similar, using inheritance might be even simpler though. Check out Philip Regan's answer.

Meta-Knight
I like that. How do I make a class conform to a protocol????
Digital Robot
thankssssssss! that will do the trick!
Digital Robot
+1  A: 

use [object setFrame:newFrame]; instead of object.frame = newFrame;
and instead of oldFrame = object.frame; use oldFrame = [object frame];

fluchtpunkt
What's wrong with dot notation?
Josh Hinman
nothing, but dot notation doesn't work with objects of type id. That's why object.frame = foo results in an compile error but [object setFrame:foo] compiles, and runs without any problems.
fluchtpunkt
+2  A: 

If you have multiple classes that share characteristics, then, if at all possible, I suggest refactoring the class structure so that those characteristics are contained in an umbrella parent class, we'll call it ClassZ. ClassZ's subclasses can override things as needed. Otherwise, let the method in the parent class handle it for you. Then, your method turns back into this...

- (void) changeColor:(ClassZ *) myOBJ toColor:(UIColor*)myColor {
   myOBJ.color = myColor; // note, myObj is ClassZ, not the subclasses.
}

Otherwise, you are stuck with id and testing the individual classes.

Philip Regan