4

Assume I have two base classes, Container and Gizmo. Class Container has an instance variable of class Gizmo.

Now I subclass Container (call that SubContainer) and I also subclass Gizmo (SubGizmo). In some of the methods of SubContainer I need to send a message to some properties that Gizmo doesn't have but SubGizmo does. Is there any way to override the ivar to be of class SubGizmo in SubContainer, so I can send those messages?

Right now I can make it work by casting my inherited ivar to SubGizmo every time I need to use such a property or method.

Here is why I want such a behavior: I already have a game that works, but the more modes I add, the harder it gets to maintain. If I want to change/add a method that will run on each mode; I would need to go to three different game - controller objects and make the change.

By subclassing, I wanted to keep the main game mechanics in the base classes and create a subclass for each mode. this way a change I make in a base class would reflect on each mode. However each controller and game object have new methods for different modes, and they send messages to each other. This is where my problem is arising from.

jscs
  • 63,694
  • 13
  • 151
  • 195
Kaan Dedeoglu
  • 14,765
  • 5
  • 40
  • 41

4 Answers4

2

just introduce type safety and conversion logic using an approach like this:

@interface SubContainer ()

- (SubGizmo *)subGizmo;
// setter is often unnecessary
- (void)setSubGizmo:(SubGizmo *)pSubGizmo;

@end

@implementation SubContainer

...

- (SubGizmo *)subGizmo
{
    Gizmo * ret = self.gizmo;
    // sanity check the type to ensure this is properly initialized,
    // or return nil if holding a Gizmo is valid in this context:
    assert([ret isKindOfClass:[SubGizmo class]]);
    return (SubGizmo *)ret;
}

- (void)setSubGizmo:(SubGizmo *)pSubGizmo
{
    self.gizmo = pSubGizmo;
}

- (void)addWater
{
  [self.subGizmo addWater];
}

@end

however, the creeping complexity suggests more variations in types is worth consideration.

justin
  • 104,054
  • 14
  • 179
  • 226
  • 1
    This is pretty neat, one other thing I saw I could do is to do SubGizmo * subg = (SubGizmo* ) gizmo; at the beginning of every method that will send such messages.. I think they pretty much do the same thing, whereas yours is more global. – Kaan Dedeoglu Apr 30 '12 at 15:17
  • @Kaan yup, when it's used in multiple places, it reduces a lot of noise and reduces the number of changes you must make if altering. – justin Apr 30 '12 at 15:20
1

just use type id for your ivar, you only have to include the proper header file to avoid warnings.

Jonathan Cichon
  • 4,396
  • 16
  • 19
  • doesn't work when I try to access properties with dot notation. I think I'll just stick to casting to the wanted subclass every time I access something that the base class doesn't have.. Thanks for the help anyways – Kaan Dedeoglu Apr 30 '12 at 06:11
0

The easiest way would be to use SubGizmo in Container, not Gizmo. :-)

However, if you can't do that directly for some reason, you can modify SubContainer at runtime (seek for class_addIvar or class_addMethod, I can give you an example when you need it), but that does not help to avoid Xcode's warning.

Kaan Dedeoglu
  • 14,765
  • 5
  • 40
  • 41
Matthias
  • 8,018
  • 2
  • 27
  • 53
  • 1
    You can NOT add an ivar to an existing class at runtime, class_addIvar is only for non registered classes. You could use objc_setAssociatedObject though – Joshua Weinberg Apr 30 '12 at 05:53
  • @Matthias yes but then I want another subclass of A ( A***) to have another instance that belongs to another subclass of B ( B*** ) , you see where I'm getting – Kaan Dedeoglu Apr 30 '12 at 05:55
  • @JoshuaWeinberg: Yes, of course, but I understood that the OP has control of the subclass. – Matthias Apr 30 '12 at 07:16
-2

You canu use NSNotifications for sending updates to all your game controllers.

Abhishek Bedi
  • 5,205
  • 2
  • 36
  • 62