I'm pretty new to Objective C.
Let's assume there are 3 class: Chef, Food, Dishs
As you may expect: A Chef cook Food -> Dishe.
- (Dishe *)cook:(Food *)food;
Now i want to add ChefFish, Fish and FishDishe class.
As you may expect: When Chef cook fish, we will have FishDishe
Naturally i would go for:
- (FishDishe *)fishTraitment:(Fish *)fish {
return [FishDishe alloc];
}
- (Dishe *)cook:(Food *)food {
if ([food isKindOfClass:[Fish class]]) {
return [self fishTraitement:food];
} else {
return [Dishe alloc];
}
}
then i got this warning:
Incompatible pointer types sending 'Food *' to parameter of type 'Fish *'
Of course, the code itself compile and run as expected.
The warning is not the real matter, as we can avoid it with casting or simply move the fishTraitment block in cook method.
But searching around, i find some topic saying using isKindOfClass is not 'clean'.
It would violate polymorphism and object orientation principles.
My question here:
What would be the best practice here ?
Note that i could have elsewhere, an array of Chef that with generic Chef and ChefFish.
I would expect:
Chef cook fish -> Dishe
ChefFish cook fish -> FishDishe
Have also though about overloading, looks like we can't have method with same name & same number of parameters (event with different type & name), which is possible in Swift :(
CodePudding user response:
There is no overloading like known from C in Objective-C.
Try to avoid declaring methods that have unused arguments. If you do either, you make only your stack waste more memory than needed.
All Objective-C objects usually inherit from NSObject at some point.
So they all inherit some methods by default. In example those: alloc
, init
, new
(calling alloc init at once), dealloc
.
@interface Cook : NSObject
-(instancetype)init; //default anyway..
@end
@interface Fish : NSObject
-(instancetype)initWithCook:(Cook*)cook; //not default
// so Fish has also one 'init' method even if not declared explicit.
@end
@interface Dish : NSObject
-(instancetype)initWithCook:(Cook*)cook;
-(instancetype)initWithCook:(Cook*)cook AndFish:(Fish*)fish;
@end
@interface SmellyFish : Fish
-(void)washSmellyDish:(Dish*)dish;
@end
You can look into the declaration of NSObject and walk thru the default methods that consequently any NSObject inherits as well.
the examples above: Cook, Dish and Fish inherit from NSObject, so you can ask for [object isKindOfClass:[NSObject class]]
which is a bit redundant as almost any objc-object inherits from NSObject at some point anyway. But SmellyFish inherits from Fish here, so you could ask if isKindOfClass
Fish, SmellyFish or NSObject and it should answer yes.
If you need to be sure some obj is of some specific class use instead
[obj isMemberOfClass:[Fish class]]
so maybe you do something like..
-(FishDish *)fishDish {
return [FishDish new];
}
-(Dish *)cook:(id)food {
if ([food isKindOfClass:[Fish class]]) {
return [self fishDish];
} else {
return [[Dish alloc] init];
}
}
see (id)food
? id
is an Objective-C specific data type that represents a C pointer of some (any kind of) NSObject
. You could also write id<SuperFood> food
expressing you expect some pointer that follows some specific protocol as object reference. Which is similar to (SuperFood*)food
, where SuperFood*
would declare it must be a pointer to one SuperFood
object.
maybe also a good practise is avoiding casting before asking for compliance to specific data types, because it is possible to make a method call to isKindOfClass worthless by just casting.
You want to check, so avoid casting.
Because if you cast to much and actually don't know the data type given in reference you are better off by just using id
or NSObject*
. This practice also keeps you from importing more code just to declare some specific datatype when you don't even use its methods in that part of implementation file.