Chapter 2 - Silver Challenge


#1

Here’s my answer to the Silver Challenge: Another initializer:

I believe this is right. However, how do I go about actually using this other initializer?

BNRItem.h

- (id) initWithItemName:(NSString *)name1 serialNumber: (NSString *)sNumber1;

BNRItem.m

- (id)initWithItemName:(NSString *)name1 serialNumber:(NSString *)sNumber1; { return [self initWithItemName: name1 valueInDollars: 0 serialNumber: sNumber1]; }


#2

Well, that’s exactly how I did it too as you can see few posts back :wink:


#3

You use your new initializer by calling it. :smiley: For example, in your main.m file for the RandomPossessions project, insert something like this:

// Create an object using our new initializer
BNRItem     *newItem = [ [ BNRItem alloc ] initWithItemName: @"Steve's Espresso Machine"
                                               serialNumber: @"XXX" ];
// Add the item to our list of random objects
[ items addObject: newItem ];

// Display the items (this should already exist in your main.m)
for( BNRItem *item in items ){
    NSLog( @"%@", item );
}

#4

My solution is pretty much the same as yours. However, in main.m I put the new item (which I called silverItem to keep track of the challenges) within the encapsulation which iterates through the array of objects. By doing this, it provides a randomValue to the newItem(silverItem)

BNRItem.h

#import <Foundation/Foundation.h>

@interface BNRItem : NSObject

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

+(id)randomItem;

// designated initializer
- (id)initWithItemName:(NSString *)name
        valueInDollars:(int)value
          serialNumber:(NSString *)sNumber;

// Silver Challenge
- (id)initWithItemName:(NSString *)nameOne
          serialNumber:(NSString *)sNumberOne;

@end

BNRItem.m

#import "BNRItem.h"

@implementation BNRItem

@synthesize itemName = _itemName;
@synthesize serialNumber = _serialNumber;
@synthesize valueInDollars = _valueInDollars;
@synthesize dateCreated = _dateCreated;

+ (id)randomItem
{
    NSArray *randomAdhectiveList = [[NSArray alloc] initWithObjects:@"Fluffy",
    @"Rusty", @"Shiny", nil];
    NSArray *randomNounList = [[NSArray alloc] initWithObjects:@"Bear", @"Spork",
    @"Mac", nil];
    
    NSInteger adjectiveIndex = rand() % [randomAdhectiveList count];
    NSInteger nounIndex = rand() % [randomNounList count];
  
    NSString *randomName = [NSString stringWithFormat:@"%@ %@",
                            [randomAdhectiveList objectAtIndex:adjectiveIndex],
                            [randomNounList objectAtIndex:nounIndex]];
    
    int randomValue = random() % 100;
    
    NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c",
                                    '0' + rand() % 10,
                                    'A' + rand() % 26,
                                    '0' + rand() % 10,
                                    'A' + rand() % 26,
                                    '0' + rand() % 10];
    
    BNRItem *newItem = [[self alloc] initWithItemName:randomName
                                       valueInDollars:randomValue
                                         serialNumber:randomSerialNumber];
    return newItem;
}

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

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

// Silver Challenge
- (id)initWithItemName:(NSString *)nameOne
          serialNumber:(NSString *)sNumberOne
{
    return [self initWithItemName:nameOne
                   valueInDollars:0
                     serialNumber:sNumberOne];
}

- (NSString *)description
{
    NSString *descriptionString = [[NSString alloc] initWithFormat:
                                   @"%@ (%@): Worth $%d, recorded on %@",
                                   _itemName, _serialNumber,
                                   _valueInDollars, _dateCreated];
    return descriptionString;
}

@end

Main.m

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

    @autoreleasepool
    {
           NSMutableArray *items = [[NSMutableArray alloc] init];

           for (int i = 0; i < 10; i++)
           {
                 BNRItem *p = [BNRItem randomItem];
                 [items addObject:p];
            
                 // Silver Challenge
                 BNRItem *silverItem = [[BNRItem alloc]
                                        initWithItemName:@"My iPad"
                                               serialNumber:@"123X4"];
                 [silverItem setValueInDollars:[p valueInDollars]];
                 [items addObject:silverItem];
             }

             for (BNRItem *item in items) 
             {
                   NSLog(@"%@", item);
             }
             items = nil;
       }
       return 0;
}

#5

[quote=“Joey2Name”]

[/code]

Main.m

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

@autoreleasepool
{
       NSMutableArray *items = [[NSMutableArray alloc] init];

       for (int i = 0; i < 10; i++)
       {
             BNRItem *p = [BNRItem randomItem];
             [items addObject:p];
        
             // Silver Challenge
             BNRItem *silverItem = [[BNRItem alloc]
                                    initWithItemName:@"My iPad"
                                           serialNumber:@"123X4"];
          [b]   [silverItem setValueInDollars:[p valueInDollars]];[/b]
             [items addObject:silverItem];
         }

         for (BNRItem *item in items) 
         {
               NSLog(@"%@", item);
         }
         items = nil;
   }
   return 0;

}

[/code][/quote]

I like how you give the new initializer a random value for the “valueInDollars” - pretty slick thinking there!

I, unfortuantly had to look at the code online to complete the challenge - I hope I get better with this! :frowning:


#6

Hi,

Question re the name of the additional initializer method, does it have to be the same as initWithItemName?
If they’re both the same, how does it know which one to execute? It is by the number of parameters?

I created mine as follows…

[code]-(id)initWithItemNameAndNumber:(NSString *)name serialNumber:(NSString *)sNumber
{
self = [self init];

if (self) {
    [self setItemName:name];
    [self setSerialNumber:sNumber];
}

return self;

}[/code]

And called it in main via;

With results of;

New item Green Sofa (A2B1C): Worth $0, recorded on 2012-10-10 08:22:47

The values for value and date are from the init and initWithItemName methods respectively.

Does this seem right?

Regards
JC


#7

HI JC,

It looks like you’re trying to create two designated initializers.

When you created the first one, your code was like this:

BNRItem.h

-(id)initWithItemName: (NSString *)name
                 serialNumber: (NSString *)sNumber
                 valueInDollars: (init)value

Then you implemented it like so:

 BNRItem.m
- (id)initWithItemName:(NSString *)name
        valueInDollars:(int)value
          serialNumber:(NSString *)sNumber
{
    self = [super init];
    
    if (self)
    {
    [self setItemName:name];
    [self setSerialNumber:sNumber];
    [self setValueInDollars:value];
    _dateCreated = [[NSDate alloc] init];
    }
    return self;
}

Is this code - you called self = [super init];

This tells the compiler, "Look for the initilizer in my super class (NSObject) - then assign it to myself (BNRItem) you then gave it default values:

[self setItemName:name]; [self setSerialNumber:sNumber]; [self setValueInDollars:value]; _dateCreated = [[NSDate alloc] init];
That was your designated init - remember, you can only have one designated init, per class.

So, you should have declared your second init like this:

[code] BNRItem.h

-(id)initWithItemName: (NSString *)name1
serialNumber: (NSString *)sNumber1
[/code]

(Notice we have not put the valueInDollars here as it is not part of the challenge.

In your implimentation file, you would have implimented it like so:

[code] BNRItem.m

-(id)initWithName: (NSString *)name1
serialNumber: (NSString *)sNumber1
{
return [self initWithName: name1 serialNumber: sNumber1];
}
[/code]

Notice how we did not call the super init again? We simply added another initializer and gave it some default values.

You are correct in a sense what when we call the second init, we can see which one it is by the amount of arguments it has. However, if you had two inits with three arguments each - how do you tell the difference?

Look at what I called the second init’s arguments in my header file (name1, sNumber1) - I created different names for them, to tell me which init is which.

I hope this helps and I haven’t just made it more confusing, LOL.


#8

Thanks,
It’s a little clearer.

So if I’m reading you comment re initialisers with the same name, this is possible as long as they have arguments with different names and different number of arguments.

Additionally, there can only be one designated init, and this one is the only one that calls [super init]
All other inits call the designated one via [self init]

Regards
JC


#9

[quote=“JonoCarnie”]Thanks,
It’s a little clearer.

So if I’m reading you comment re initialisers with the same name, this is possible as long as they have arguments with different names and different number of arguments.
[/quote]

Yes. But they can all have same number of arguments - that doesn’t matter. But the arguments must have different names, so you can identify them.

[quote=“JonoCarnie”]
Additionally, there can only be one designated init, and this one is the only one that calls [super init]
All other inits call the designated one via [self init]

Regards
JC[/quote]

Spot on. :wink:


#10

But CAN the initializers have different names? I named my new initializer initWithNameAndSerialNumber and used the same argument names as in the default initializer. It seems to work fine. Is this not the preferred way?


#11

I think it’s down to preference?

Both ways seem acceptable to me.


#12

Here are three distinct method names

init
initWithItemName:serialNumber:
initWithItemName:valueInDollars:serialNumber:

These names are not the same, they are distinct.
Also notice that the method names also include the colon “:” when one repeats saying them, the colon is part of the name when said! and the entire string is what makes the name of the method.

Where you are getting confused, is that you are thinking of these method names as being made up with by more then one name.

“initWithItemName:valueInDollars:serialNumber:” is one single method name, even though it expects 3 arguments, and is split up into 3 portions.
Arguments are interleaved with the method name only for the sake of making it easier for a user to match up the arguments with what they represent.
This is all part of a naming convention.

I also believe (although Im not 100% sure) that the name chosen to represent the argument passed into a method, are local variables and only exist inside the method. So therefore two methods may have the same name for an argument, and everything works fine because they are local variables and their value does not effect other uses of the same variable name outside the body of the method.

Also, one has to be careful not to use instance variable and/or global variable names as argument variable names, because instant variables and global variables can be accessed inside the method body. Note that when I say instant variable, I am referring to instant variables that belong to the same instant which has the method in question.

I hope this helps, and if Im wrong, please point me straight.