-3

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 :(

Fernand
  • 103
  • 1
  • 6
  • Does this answer your question? [How to cast an object in Objective-C](https://stackoverflow.com/questions/690748/how-to-cast-an-object-in-objective-c) – Raphael Schweikert Mar 18 '22 at 11:53
  • This could help me to silent the warning if i want keep the actual code, i have also though about casting but some saying class casting in almost always bad idea & dirt. Curious to know if there is better solution/ architecture. But still thank for it ! – Fernand Mar 18 '22 at 12:04
  • Thanks for the clarification. In this case, the question isn’t really language-dependent. `isKindOfClass` is like `instanceof` in other languages. It violates OOP principles only insofar as it’s often used in cases where polymorphism is a better solution. If food knows how to cook itself into a dish, it would be possible to call `[food cook]` on all kinds of food to get a dish but it would be less clean wrt/ no cook being involved. – Raphael Schweikert Mar 18 '22 at 12:24
  • So I guess what I’m trying to say is: everything’s a trade-off. And absolute statements like “using `isKindOfClass` always violates OOP principles” don’t get us anywhere. If you your `isKindOfClass` works for you and there isn’t obvious repetition, leave it. As soon as it becomes a burden, refactor it. – Raphael Schweikert Mar 18 '22 at 12:28
  • One last note: the most OOP-sanctioned way of doing this would probably to have a chef factory that hands out chefs with varying specialties. You would pass in a fish to the chef factory which hands you a chef capable of cooking fish into fish dishes, which you could then ask to prepare a fish dish for you. However, thinking of how this would be implemented, it would still require both the instance check and the cast, so even by applying best-practice design pattern, you haven’t gotten rid of any of the code smells (let alone the fish stink). – Raphael Schweikert Mar 18 '22 at 12:32
  • 2
    I retracted my close vote as the rephrased question is no longer a duplicate. However, as it stands, while I think your question is an interesting one, it’s probably a bad fit for stackoverflow; because questions for best-practice often attract opinion-based answers. – Raphael Schweikert Mar 18 '22 at 12:36
  • Thanks for all theses explanation, it's really hard for a beginner like me to take a decision, so even if an opinion-based answers it's always helpful especially when i'm not confident with the language. Your comment is exactly what i'm waiting for, i would accepted the answer if you could regroup all these comment into one. (i know it's not 100% language related, reason why i've tagged it with oop) – Fernand Mar 18 '22 at 13:39

1 Answers1

0

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]; //FishDish must be a subclass of Dish 
    } 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.

Ol Sen
  • 3,163
  • 2
  • 21
  • 30