Gold Challenge: Another Class


#1

This took me bloody ages -and I’m still not happy with the formatting in the console log output. Anyways here’s what I got:

HEADER FILE

[code]//
// BNRContainer.h
// RandomPossesion

#import <Foundation/Foundation.h>
#import “BNRItem.h”

@interface BNRContainer : BNRItem {
NSString *containerName;
NSMutableArray *containerArray;
}

  • (void)setContainerArray:(id)item;

  • (NSMutableArray *)containerArray;

  • (void)setContainerName:(NSString *)name;

  • (NSString *)containerName;

// Initializer methods

  • (id)initWithContainerName:(NSString *)name;
    @end[/code]

IMPLEMENTATION FILE

[code]//
// BNRContainer.m
// RandomPossesion

#import “BNRContainer.h”

@implementation BNRContainer

// New designated initializer

  • (id)initWithContainerName:(NSString *)name {
    // Call the super init
    self = [super init];

    // Was self properly initialized ?
    if (self) {
    self.containerName = name;
    self.serialNumber = @" ";
    self.valueInDollars = 0;
    dateCreated = [[NSDate alloc] init];
    // Need to initialize the array here otherwise f**k all happens
    containerArray = [[NSMutableArray alloc] init];
    }
    return self;
    }

// Accessor methods

  • (void)setContainerArray:(id)item {
    [containerArray addObject:item];
    }

  • (NSMutableArray *)containerArray {
    return containerArray;
    }

  • (void)setContainerName:(NSString *)name {
    containerName = name;
    }

  • (NSString *)containerName {
    return containerName;
    }

// Overidden valueInDollars methods from BNRItem superclass

  • (int)valueInDollars {
    int value = 0;
    for (int i = 0; i < [containerArray count]; i++) {
    value += [[containerArray objectAtIndex:i] valueInDollars];
    }
    return value;
    }

// Description method. Not declared in .h because it overides the NSObject superclass method

  • (NSString *)description {

    NSString *descriptionString = [[NSString alloc] initWithFormat:@" %@ Value: $%d Created on: %@ Items in %@ %@", self.containerName, self.valueInDollars, self.dateCreated, self.containerName, self.containerArray];
    return descriptionString;
    }

@end
[/code]

MAIN

[code]//
// main.m
// RandomPossesion

#import <Foundation/Foundation.h>
#import “BNRItem.h”
#import “BNRContainer.h”

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

@autoreleasepool {
    
    // Instantiate three new BNRItems for containerOne
    BNRItem *itemOne = [BNRItem randomItem];
    BNRItem *itemTwo = [BNRItem randomItem];
    BNRItem *itemThree = [BNRItem randomItem];
    
    // Instantiate three new BNRItems for containerTwo
    BNRItem *itemFour = [BNRItem randomItem];
    BNRItem *itemFive = [BNRItem randomItem];
    BNRItem *itemSix = [BNRItem randomItem];
    
    // Instantiate two new BNRContainers
    BNRContainer *containerOne = [[BNRContainer alloc] initWithContainerName:@"Container One"];
    BNRContainer *containerTwo = [[BNRContainer alloc] initWithContainerName:@"Container Two"];
    
    // Add three BNRItems to containerOne's containerArray
    containerOne.containerArray = itemOne;
    containerOne.containerArray = itemTwo;
    containerOne.containerArray = itemThree;
    
    // Add three BNRItems to containerTwo's containerArray
    containerTwo.containerArray = itemFour;
    containerTwo.containerArray = itemFive;
    containerTwo.containerArray = itemSix;
    
    // Add containerTwo to containerOne's array
    containerOne.containerArray = containerTwo;
    
    
    // Log containerOne's description
    NSLog(@"%@", containerOne);

}
return 0;

}[/code]


#2


#3


#4

Heh, I thought I would get “cute” and have a container update it’s own value every time something got added to it and that it would somehow be more efficient the doing the calculation on the fly, but after thinking about it it’s just one of those darned if you do, darned if you don’t thing. Either the total calculation is “expensive” when you hit that accessor, or calling the “addItem” is potentially expensive and you have to implement something to run up the entire chain of parent containers to update each of their values. I think your way is simpler, so I like it better.


#5

Hi all. I am overriding the description method for BNRContainer, similarly to what has been posted before:

- (NSString *) description
{
    
    NSString *descString = [[NSString alloc] 
                            initWithFormat:@"\n > %@  SN: %@ Value of container: $%d Total Value: $%d Subitems %@", 
                            self.itemName, self.serialNumber, self.valueOfContainer,
                            self.valueInDollars, self.subItems];

    return descString;
}

The problem is that the output in the console is way too ugly. I have a big box container and a small box within it. The output does not deal properly with the line-breaks (\n) when called for the second container:

2012-04-06 23:14:00.094 RandomPossessions[11513:403] 
 > Big Box  SN: A1A1A1 Value of container: $25 Total Value: $287 Subitems (
    "Rusty Spork (8Q2U8): Worth $73",
    "Shiny Spork (5Y2V3): Worth $40",
    "\n > Small box  SN: B1B1B1 Value of container: $10 Total Value: $149 Subitems (\n    \"Rusty Spork (2F9Z7): Worth $40\",\n    \"Rusty Bear (8G5V6): Worth $99\"\n)"
)

Any ideas? Is this also happening to you?


#6

I can’t test right now, but can you try replacing \n with \n?


#7

Thanks, Joe. Then, the output is:

2012-04-06 23:14:58.288 RandomPossessions[11526:403] \n > Big Box  SN: A1A1A1 Value of container: $25 Total Value: $287 Subitems (
    "Rusty Spork (8Q2U8): Worth $73",
    "Shiny Spork (5Y2V3): Worth $40",
    "\\n > Small box  SN: B1B1B1 Value of container: $10 Total Value: $149 Subitems (\n    \"Rusty Spork (2F9Z7): Worth $40\",\n    \"Rusty Bear (8G5V6): Worth $99\"\n)"
)

Strangely enough, if I remove my \n in the method, still there are \n’s in the console:

2012-04-06 23:16:33.213 RandomPossessions[11548:403]  > Big Box  SN: A1A1A1 Value of container: $25 Total Value: $287 Subitems (
    "Rusty Spork (8Q2U8): Worth $73",
    "Shiny Spork (5Y2V3): Worth $40",
    " > Small box  SN: B1B1B1 Value of container: $10 Total Value: $149 Subitems (\n    \"Rusty Spork (2F9Z7): Worth $40\",\n    \"Rusty Bear (8G5V6): Worth $99\"\n)"
)

This seems related to how the %@ is recursively expanded within a string, or something like that.


#8

[quote=“spikeygoat”]This took me bloody ages -and I’m still not happy with the formatting in the console log output.
…[/quote]

Maybe a similar problem to the one I reported above?


#9


#10


#11

Thanks. Clearly, using NSLog in the “description” method helps to avoid the problem. However, I have the feeling that the “description” method should just return a string and not cause side effects like printing to the console. In any case, it seems that, for total control of the output, there is no other way.

I’ll try recursion later for indenting. I guess it is about adding tabs after each recursive call is made.


#12


#13

Thanks, greenSpaceAlien. Beautiful output. I used to program in LISP a few years ago so I should have few problems with recursion for implementing something like you suggested.
I still can’t stop thinking that the spirit of this “gold challenge” is to solve it by invoking the description methods through the %@, and let the runtime system to figure out when it’s dealing with an item or a container.


#14

Here is my solution. I know that an array can contain pointers to different objects, but in the case of a BNRContainer, we would only want a BNRItem to be in the container. In my example I used “id” as the parameter for adding/removing items, but I think in this case it should really be a “BNRItem *” instead of an “id”. Thoughts?

BNRContainer.h

#import "BNRItem.h"

@interface BNRContainer : BNRItem
{
    NSString *containerName;
    NSMutableArray *containerItems;
}

@property (copy, nonatomic) NSString *containerName;
@property NSMutableArray *containerItems;

#pragma mark - Initializers
//designated initializer
- (id) initWithContainerName:(NSString *)name; 

#pragma mark - Instance Methods
- (int)totalValueInDollars;
- (void)addItemToContainer:(id)itemToAdd;
- (void)removeItemFromContainer:(id)itemToRemove;

@end

BNRContainer.m

#import "BNRContainer.h"

@implementation BNRContainer

@synthesize containerName, containerItems;

#pragma mark - Initializers
//Must override the superclass's initializer because this class implements it's own designiated initializer
- (id) init
{
    return [self initWithContainerName:@"Container"];
}

//designated initializer
- (id)initWithContainerName:(NSString *)name
{
    //call the superclass's initializer
    self = [super init];
    
    //validate
    if (self) {
        [self setContainerName:name];
        [self setContainerItems:[[NSMutableArray alloc] init]];
    }
    
    //return the address of the newly initialized object
    return self;
}

#pragma mark - Instance Methods
//Represents the total value in dollars of all the items in the container
- (int)totalValueInDollars
{
    int value = 0;
    
    //check the array
    if (!containerItems || [containerItems count] == 0) {
        return value;
    }
    
    //Get the total value
    for (BNRItem *b in containerItems)
    {
        value += [b valueInDollars];
    }
    
    //return result
    return value;
}

//Adds a new item to the container
- (void)addItemToContainer:(id)itemToAdd
{
    [containerItems addObject:itemToAdd];
}


//Removes an item from the container
- (void)removeItemFromContainer:(id)itemToRemove
{
    [containerItems removeObject:itemToRemove];
}

#pragma mark - Overrides
- (NSString *)description
{
    //Build a human readable description
    NSString *descriptionString = [[NSString alloc] initWithFormat:@"Container\n Name: %@\n Value: %d\n Items:\n %@", 
                                   [self containerName], 
                                   [self totalValueInDollars],
                                   containerItems];
    
    return descriptionString;
}

@end

main.m

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

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

    @autoreleasepool {
        
        //create a new container
        BNRContainer *possessions = [[BNRContainer alloc] initWithContainerName:@"My Stuff"];
        
        //Add a few items to the container
        for (int i = 0; i < 10; i++) {
            BNRItem *b = [BNRItem randomItem];
            [possessions addItemToContainer:b];
        }
        
        //Display the container, it's value, and the items it contains
        NSLog(@"%@", possessions);
    }
    return 0;
}

#15

Would it be possible to solve the Gold challenge creating a new “file” for the BNRContainer class ? Creating it as a subclass of BNRItem ?
I understand that was not in the scope of chapter 2, but I think it was presented like this in “Objective-C Programming” on a similar exercise …

[edit] : I suppose that in that case, the BNRContainer class must be created as a subclass of NSObject, not BNRItem, right ?

Thanks
Fred


#16

So I’m trying to create a file for the BNRContainer class, but I’m stuck : my container won’t initialize …

Here are my files:

BNRItem.h:

[code]#import <Foundation/Foundation.h>

@interface BNRItem : NSObject
{
NSString *itemName;
NSString *serialNumber;
int valueInDollars;
NSDate *dateCreated;
}

@property (nonatomic) NSString *itemName;
@property (nonatomic) NSString *serialNumber;
@property int valueInDollars;
@property (nonatomic) NSDate *dateCreated;

  • (id)randomItem;
  • (id)initWithItemName:(NSString *)name
    valueInDollars:(int)value
    serialNumber:(NSString *)sNumber;

  • (id)initWithItemName:(NSString *)name
    serialNumber:(NSString *)sNumber;

@end[/code]

BNRItem.m:

[code]#import “BNRItem.h”

@implementation BNRItem

@synthesize itemName, serialNumber, valueInDollars, dateCreated;

  • (id)randomItem
    {
    // Create an array of three objectives
    NSArray *randomAdjectiveList = [NSArray arrayWithObjects:@“Fluffy”
    @“Rusty”,
    @“Shiny”, nil];

    // Create an array of three nouns
    NSArray *randomNounList = [NSArray arrayWithObjects:@“Bear”
    @“Spork”,
    @“Mac”, nil];

    // Get the index of a random adjective/noun from the lists
    // Note : The % operator, called the modulo operator, gives
    // you the remainder. So adjectiveIndex is a random number
    // from 0 to 2 inclusive
    NSInteger adjectiveIndex = rand() % [randomAdjectiveList count];
    NSInteger nounIndex = rand() % [randomNounList count];

    // Note that NSInteger is not an object, but a type definition
    // for “unsigned long”

    NSString *randomName = [NSString stringWithFormat:@"%@ %@",
    [randomAdjectiveList objectAtIndex:adjectiveIndex],
    [randomNounList objectAtIndex:nounIndex]];

    int randomValue = rand() % 100;

    NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c",
    ‘0’ + rand() % 10,
    ‘A’ + rand() % 26,
    ‘0’ + rand() % 10,
    ‘B’ + rand() % 26,
    ‘0’ + rand() % 10];

    BNRItem *newItem = [[self alloc]initWithItemName:randomName
    valueInDollars:randomValue
    serialNumber:randomSerialNumber];
    return newItem;

}

// The designated initializer

  • (id)initWithItemName:(NSString *)name
    valueInDollars:(int)value
    serialNumber:(NSString *)sNumber
    {
    // Call the superclass’s designated initializer
    self = [super init];

    // Did the superclass’s designated initializer succeed ?
    if (self) {
    // Give the instance variables initial values
    [self setItemName:name];
    [self setSerialNumber:sNumber];
    [self setValueInDollars:value];
    dateCreated = [[NSDate alloc]init];
    }

    // Return the address of the newly initialized object
    return self;
    }

//Override init

  • (id)init
    {
    return [self initWithItemName:@“Item"
    valueInDollars:0
    serialNumber:@”"];
    }

  • (NSString *)description
    {
    NSString *descriptionString =
    [[NSString alloc]initWithFormat:@"%@ (%@) : Worth $%d, recorded on %@",
    itemName,
    serialNumber,
    valueInDollars,
    dateCreated];
    return descriptionString;
    }

// Other initializer

  • (id)initWithItemName:(NSString *)name
    serialNumber:(NSString *)sNumber
    {
    return [self initWithItemName:name
    valueInDollars:0
    serialNumber:sNumber];}

@end
[/code]

BNRContainer.h

[code]#import “BNRItem.h”
@class BNRItem;

@interface BNRContainer : BNRItem {
NSMutableArray *container;
}

@property NSMutableArray *container;

  • (void)addItemToContainer:(BNRItem *)item;
  • (int)valueOfContainer;

@end
[/code]

BNRContainer.m:

[code]#import “BNRContainer.h”
#import “BNRItem.h”

@implementation BNRContainer

@synthesize container;

  • (void)addItemToContainer:(BNRItem *)item {
    if (!container) {
    container = [[NSMutableArray alloc]init];
    }
    [container addObject:item];
    }

  • (int)valueOfContainer {
    unsigned int sum = 0;
    for (BNRItem *item in container) {
    sum+= [item valueInDollars];
    }
    return sum;
    }

  • (NSString *)description {
    return [NSString stringWithFormat:@“Container %@: $%i of value”, self, [self valueOfContainer]];
    }

@end
[/code]

Main.m:

[code]#import <Foundation/Foundation.h>
#import “BNRItem.h”
#import “BNRContainer.h”

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

@autoreleasepool {
    
    // Create a mutable array object, store its address in items variable
    NSMutableArray *items = [[NSMutableArray alloc]init];
    
    
    for (int i = 0; i < 10; i++) {
        BNRItem *p = [BNRItem randomItem];
        [items addObject:p];
    }
    
    // Create a container
    BNRContainer *backpack = [[BNRContainer init]alloc];
    
    for (BNRItem *item in items) {
        NSLog(@"%@", item);
        [backpack addItemToContainer:item];
    }
        
    NSLog(@"Container %@ has a value of %i", backpack, [backpack valueOfContainer]);

            
    // Destroy the array pointed to by items
    items = nil;
    
}

return 0;

}
[/code]

My problem : Build succeeded, but I encountered an exception at runtime :

[quote]2012-05-20 18:36:45.305 RandomPossessions[15593:403] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** +[BNRContainer<0x10770a660> init]: cannot init a class object.’
*** First throw call stack:
(
0 CoreFoundation 0x00007fff93532f56 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff9370ad5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff935beece +[NSObject init] + 174
3 RandomPossessions 0x0000000107708711 main + 273
4 RandomPossessions 0x00000001077085f4 start + 52
5 ??? 0x0000000000000001 0x0 + 1
)
terminate called throwing an exception(lldb) [/quote]

It seems my class BNRContainer has not been correctly declared …
Can you help me ?

Thanks
Fred


#17

I’m stupid : BNRContainer *backpack = [[BNRContainer init]alloc];
does not work as well as
BNRContainer *backpack = [[BNRContainer alloc]init];

:slight_smile:


#18

ok, so here is my problem:

BNRContainer.h

[code]#import “BNRItem.h”

@interface BNRContainer : BNRItem
{
NSMutableArray *subItems;
NSString *containerName;
}

  • (id)initWithContainerName:(NSString *)containerName;

  • (int)totalValue;

  • (void)addItemsToContainer:(NSMutableArray *)sItems;

  • (void)setSubItems:(NSMutableArray *)sItems;

  • (NSMutableArray *)subItems;

  • (void)setContainerName:(NSString *)cName;

  • (NSString *)containerName;

@end
[/code]

BNRContainer.m

[code]#import “BNRContainer.h”

@implementation BNRContainer

  • (id)initWithContainerName:(NSString *)cName
    {
    self = [super init];
    if (self) {
    [self setContainerName:cName];
    [self setSubItems: [[NSMutableArray alloc]init]];
    }
    return self;
    }

  • (id)init{
    return [self initWithContainerName:@“needs name”];
    }

  • (void)addItemsToContainer:(id)bItems
    {
    [subItems addObject:bItems];
    }

  • (void)setSubItems:(NSMutableArray *)sItems
    {
    subItems = sItems;
    }

  • (NSMutableArray *)subItems
    {
    return subItems;
    }

  • (void)setContainerName:(NSString *)cName
    {
    containerName = cName;
    }

  • (NSString *)containerName
    {
    return containerName;
    }

  • (int)totalValue
    {
    // y si no hay items?
    int valueAcumulated = valueInDollars;
    for (int i = 0; i < [subItems count]; i++) {
    valueAcumulated += [[subItems objectAtIndex:i] valueInDollars];
    }
    return valueAcumulated;
    NSLog(@"%d", valueAcumulated);
    }

  • (NSString *)description
    {
    NSString *descriptionString = [[NSString alloc]initWithFormat:@“Container: %@ with value: %d, and items detail %@”,
    [self containerName], [self totalValue], subItems];
    return descriptionString;

}

@end
[/code]

and main

[code]#import <Foundation/Foundation.h>
#import “BNRItem.h”
#import “BNRContainer.h”

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

@autoreleasepool {
    
    BNRContainer *containerOne = [[BNRContainer alloc] initWithContainerName:@"firstContainer"];
    
    for (int i = 0; i < 10; i++) {
        BNRItem *b = [BNRItem randomItem];
        [containerOne addItemsToContainer:b];
    }
        NSLog(@"%@", containerOne);
    
    containerOne = nil;
    
    
}
return 0;

}[/code]

It works but I get this warning: main.m:22:47: Incompatible pointer types sending ‘BNRItem *__strong’ to parameter of type ‘NSMutableArray *’

Any ideas??


#19

Im having the same problem with the “\n” not getting printed out as a new line.

Im beginning to suspect that this is a problem with in NSLog, and how it handles recursion.