How did I do on the Gold Challenge?


#1

The gold challenge wasn’t… too bad. The thing that really kicked my ass was the NSMutableArray in my BNRContainer. I wrote all my logic, but didn’t add a designated initializer. So the memory for my NSMutableArray wasn’t getting allocated. It just get reporting null when I inspected it in the debugger. I fear the memory stuff is going to be the bane of my existence for awhile. I’m a programmer, but I come from the web side of things. So I’m used to dynamic typed/garbage collected languages (PHP and Ruby mainly). I’ll just have to get used to this mindset (hopefully it becomes second nature at some point soon).

Anyway, this is my second foray into Objective-C/Cocoa/Cocoa Touch, but this is my first real try. The last time I just skimmed through Learning iOS Development series from APress.

So how did I do on my gold challenge? It ultimately works (although I haven’t tried adding a BNRContainer to my BNRContainer yet…). Feel free to critique me on the littlest details.

BNRContainer.h

#import <Foundation/Foundation.h>
#import "BNRItem.h"

@interface BNRContainer : BNRItem
{
  NSMutableArray *subItems;
}

- (id)initWithContainerName:(NSString *)name 
    valueInDollars:(int)value 
      serialNumber:(NSString *)sNumber;

- (void)addItem:(BNRItem *)item;

- (NSMutableArray *)subItems;

@end

BNRContainer.m

#import "BNRContainer.h"

@implementation BNRContainer

- (id)init
{
  return [self initWithName:@"Container" valueInDollars:0 serialNumber:@""];
}

- (id)initWithContainerName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber
{
  self = [super init];
  
  if (self) {
    [self setItemName:name];
    [self setValueInDollars:value];
    [self setSerialNumber:sNumber];
    subItems = [[NSMutableArray alloc] init];
  }
  
  return self;
}

- (void)addItem:(BNRItem *)item
{
  [subItems addObject:item];
}

- (NSMutableArray *)subItems
{
  return subItems;
}

- (int)valueInDollars
{
  int totalValue = valueInDollars;
  for (int i = 0; i < [subItems count]; i++) {
    totalValue += [[subItems objectAtIndex:i] valueInDollars];
  }
  
  return totalValue;
}

- (NSString *)description
{
  NSString *descriptionString = 
      [[NSString alloc] initWithFormat:@"\n%@ (%@): Worth $%d, recorded on %@\nContains the following items:\n\t%@",
                             itemName,
                             serialNumber,
                             [self valueInDollars],
                             dateCreated,
                             [self subItems]];
  
  return descriptionString;
}

@end

main.m

#import <Foundation/Foundation.h>
#import "BNRContainer.h"

int main(int argc, const char * argv[])
{

  @autoreleasepool {
      
    // Gold Challenge {{1
    BNRContainer *items = [[BNRContainer alloc] initWithContainerName:@"My Container" 
                                                       valueInDollars:500 
                                                         serialNumber:@"0A1B2"];
    
    for (int i = 0; i < 10; i++) {
      BNRItem *p = [BNRItem randomItem];
      [items addItem:p];
    }
    
    NSLog(@"%@", items);
    
    items = nil;
    // End Gold Challenge 1}}
  } 
  
  return 0;

}

:blush: Have at it.


#2

Excellent! Except you missed one little thing.

[code]-(id)initWithContainerName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber {
self = [super init];

if (self) {
    [self setItemName:name];
    [self setValueInDollars:value];
    [self setSerialNumber:sNumber];
    subItems = [[NSMutableArray alloc] init];
   dateCreated = [[NSDate alloc] init];
}
return self;

}[/code]

You forgot the dateCreated = [[NSDate alloc] init]; in your BNRContainer.m file otherwise you Container Date Created is NULL.


#3

[quote=“mike274”]Excellent! Except you missed one little thing.

[code]-(id)initWithContainerName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber {
self = [super init];

if (self) {
    [self setItemName:name];
    [self setValueInDollars:value];
    [self setSerialNumber:sNumber];
    subItems = [[NSMutableArray alloc] init];
   dateCreated = [[NSDate alloc] init];
}
return self;

}[/code]

You forgot the dateCreated = [[NSDate alloc] init]; in your BNRContainer.m file otherwise you Container Date Created is NULL.[/quote]

Doh! Thank you. This is what happens when you force yourself to work through the problem past your normal bed time ha. If I remember correctly, in the introduction of the book they even mention getting good sleep, this is why! Thanks for that Mike. I’m going to go ahead and add that now.

Edit - I decided to figure out why I could have overlooked that. Certainly I would have noticed if the output in my debugger was missing a date… I ran the source code and it stood and my BNRContainer has a dateCreated… I’m guessing this is because the call to [super init]? I mean, it makes sense because of inheritance. Is this proper? or should I explicitly set this class property in my initializer?


#4

Remember that the initializer rules say that a designated initializer of a class MUST call the designated initializer of the superclass. In this case, BNRContainer’s designated initializer is initWithContainerName:valueInDollars:serialNumber:. Within this method, you call [super init]… but init is not the designated initializer of BNRItem.

Instead, you should be calling [super initWithItemName:name valueInDollars:value serialNumber:sNumber]; That way, your initializer for BNRContainer just looks like this:

-(id)initWithContainerName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber {
    self = [super initWithItemName:name valueInDollars:value serialNumber:sNumber];
    
    if (self) {
        subItems = [[NSMutableArray alloc] init];
    }
    return self;
}

And you get the rest of the instance variable set for free from the superclass’ implementation. You may not even want to change the name of the designated initializer to initWithContainerName:… and instead leave it as initWithItemName:… but override it to run the same code above. Otherwise, if you do choose to leave it as initWithContainerName:…, you must override initWithItemName:… in BNRContainer.m.

- (id)initWithItemName:(NSString *)n valueInDollars:(int)v serialNumber:(NSString *)s
{
    return [self initWithContainerName:n valueInDollars:v serialNumber:s];
}

#5

[quote=“JoeConway”]Remember that the initializer rules say that a designated initializer of a class MUST call the designated initializer of the superclass. In this case, BNRContainer’s designated initializer is initWithContainerName:valueInDollars:serialNumber:. Within this method, you call [super init]… but init is not the designated initializer of BNRItem.

Instead, you should be calling [super initWithItemName:name valueInDollars:value serialNumber:sNumber]; That way, your initializer for BNRContainer just looks like this:

-(id)initWithContainerName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber {
    self = [super initWithItemName:name valueInDollars:value serialNumber:sNumber];
    
    if (self) {
        subItems = [[NSMutableArray alloc] init];
    }
    return self;
}

And you get the rest of the instance variable set for free from the superclass’ implementation. You may not even want to change the name of the designated initializer to initWithContainerName:… and instead leave it as initWithItemName:… but override it to run the same code above. Otherwise, if you do choose to leave it as initWithContainerName:…, you must override initWithItemName:… in BNRContainer.m.

[code]

  • (id)initWithItemName:(NSString *)n valueInDollars:(int)v serialNumber:(NSString *)s
    {
    return [self initWithContainerName:n valueInDollars:v serialNumber:s];
    }
    [/code][/quote]

Thanks for the clarification Joe! (And thanks for the amazing book. I’ve tried to learn iOS Development before, but this book made sense of EVERYTHING so far).

I thought I had read that a class couldn’t implement an initializer with the same exact name as it’s super class, but I guess I should have just referenced the book when I thought that.