tags:

views:

132

answers:

2

Hi,

Code below is pseudo code. Imagine a class "Fruit" which has a factory method to create a fruit.

interface Fruit
{
}
+(Fruit*) createFruit:
{
  return [[Fruit alloc] init autorelease];
}

Now I want to subclass the Fruit to get an Apple:

interface Apple : Fruit
{
int iSeeds;
}
+(Apple*) createAppleWithColor: (int) iSeeds
{
Apple* oApple = [Apple createFruit:];
oApple.iSeeds = iSeeds;
return oApple;
}

Questions:

  • How can I make "iSeeds" private so it cannot be changed from outside? If I add a "private" keyword it does not build anymore.
  • Still I want to set iSeeds from inside my Apple's factory method.
  • I want users allow to READ the content of iSeeds. So I suppose I should have a getter but I can't get it to work. I always get some error about "LValue assignment".
  • The Fruit's createFruit is making use of autorelease. Does the Apple have to reatin/release anything?

René

+1  A: 

A couple things.

  1. Your createFruit method is wrong. It should be:

    + (Fruit *) createFruit {
      //autorelease, because the Create Rule only applies to CoreFoundation functions
      return [[[Fruit alloc] init] autorelease];
    }
  2. Instance variables are @protected by default. That means they can be directly accessed by the class and any subclasses. You can make it @protected, @private, @public, and @package. You do so like this:

    @interface Apple : Fruit {
      @private
      int iSeed
    }
    ...
    @end
  3. If you want an instance variable to be readonly, then don't declare it as @public, but make a getter for it. For example:

    - (int) iSeeds {
      return iSeeds;
    }
  4. However, since the instance variable is readonly, you can't set it externally. The way around this is to give the Apple a iSeed value during initialization:

    - (id) initWithSeeds:(int)aniSeedValue {
      if (self = [super init]) {
        iSeed = aniSeedValue;
      }
      return self;
    }

    Then make your createAppleWithColor:(int)iSeeds method like:

    + (Fruit *) createAppleWithColor:(int)iSeeds {
      return [[[Apple alloc] initWithSeeds:iSeeds] autorelease];
    }
  5. Finally, you should read the Naming Conventions guide and the Memory Management guide.

Dave DeLong
The convenience constructor would normally just be `fruit`.
Chuck
@Chuck, agreed. I just added a link to go check out the Naming Conventions :)
Dave DeLong
I see. I still have a lot to learn.Regarding naming conventions: I'll have to see if I can live with them. I'm coming from C# and currently I'm not willing to accept this obscure ObjectiveC style, but eventually I will adopt to it. :-)What I don't get at all: "createAppleWithColor" is a method in the Apple class. Why can't it acces Apple's iSeed member? In C# it does not matter if a method is static, it is still a member of the class.
Krumelur
@Krumelur - you can do `apple->iSeeds = iSeeds` if you were to declare the instance variable as `@public`. However, that reeks of bad design...
Dave DeLong
+1  A: 

One more thing. If you have a factory method which applies to a class and its subclasses, you can (probably should) do this:

+(Fruit*) fruit
{
    [[[self alloc] init] autorelease];
}

This means that if you invoke the method with a subclass, you'll get an object of the right type e.g.

Fruit* apple = [Apple fruit];

Of course, you'll need to provide an init method for Apple that provides a suitable default value for iSeeds and then invokes -initWithSeeds:

JeremyP