Challenge - difference in array creation


#1

I see I went about the array creation a little differently. (I think I reviewed all the threads, but didnā€™t notice it done this way)

Since I got the correct result I assume it is a valid alternative, dangerous I know! :smiling_imp:

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

        // Create an array with three instances of StockHolding 
        // Create an empty array
        NSMutableArray *stockList = [NSMutableArray array];
        
        // Add 3 instances of stockHolding to the array
        for (int i = 0; i < 3; i++) {
            [stockList addObject:[[StockHolding alloc] init]];
        }
        
        // Give the instance variables values        
        // 1st purchase 2.3, current 4.5, number of shares 40
        [[stockList objectAtIndex:0] setPurchaseSharePrice:2.3];
        [[stockList objectAtIndex:0] setCurrentSharePrice:4.5];
        [[stockList objectAtIndex:0] setNumberOfShares:40];
        
        // 2nd 12.19, 10.56, 90
        [[stockList objectAtIndex:1] setPurchaseSharePrice:12.19];
        [[stockList objectAtIndex:1] setCurrentSharePrice:10.56];
        [[stockList objectAtIndex:1] setNumberOfShares:90];

        // 3rd 45.10, 49.51, 210
        [[stockList objectAtIndex:2] setPurchaseSharePrice:45.10];
        [[stockList objectAtIndex:2] setCurrentSharePrice:49.51];
        [[stockList objectAtIndex:2] setNumberOfShares:210];
            
        // Iterate through the array printing out the value of each
        for (int i = 0; i < 3; i++) {
            NSLog(@"The current value of the stock is %.2f dollars", [[stockList objectAtIndex:i] valueInDollars]); 
        }        
    }
   
    return 0;
}

#2

With ARC turned off, you should have received at least some ominous warning messages from the compiler;
with ARC turned on, your code would not even compile.

The reason is that you are loosing valuable static type information by not invoking the methods on the object instances directly.

// No type information
[stockList addObject:@"I will cause havocs!"];
...
[[stockList objectAtIndex:0] setNumberOfShares:40];

// As opposed to
StockHolding *stockHolding = [StockHolding] alloc] init ...];
[stockHolding setNumberOfShares:40];
...

What version of Xcode are you using?


#3

Well thatā€™s interestingā€¦

I got no warnings, no errors at all.

I am using Xcode Version 4.3.3 (4E3002)


#4

The reason is that somehow the compiler managed to deduce that stockList is an array of instances of StockHolding class. I am impressed, but then it silently allows you to insert dangerous goods into the array:

#import <Foundation/Foundation.h>

@interface StockHolding: NSObject
- (void)setPurchaseSharePrice:(float)v;
- (void)setCurrentSharePrice:(float)v;
- (void)setNumberOfShares:(float)v;
- (float)valueInDollars;
@end

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        
        // Create an array with three instances of StockHolding 
        // Create an empty array
        NSMutableArray *stockList = [NSMutableArray array];
        
        // Add 3 instances of stockHolding to the array
        for (int i = 0; i < 3; i++) {
            [stockList addObject:@"Dangerous good!"];
            [stockList addObject:[[StockHolding alloc] init]];
        }

        // Give the instance variables values        
        // 1st purchase 2.3, current 4.5, number of shares 40
        [[stockList objectAtIndex:0] setPurchaseSharePrice:2.3];
        [[stockList objectAtIndex:0] setCurrentSharePrice:4.5];
        [[stockList objectAtIndex:0] setNumberOfShares:40];
        
        // 2nd 12.19, 10.56, 90
        [[stockList objectAtIndex:1] setPurchaseSharePrice:12.19];
        [[stockList objectAtIndex:1] setCurrentSharePrice:10.56];
        [[stockList objectAtIndex:1] setNumberOfShares:90];
        
        // 3rd 45.10, 49.51, 210
        [[stockList objectAtIndex:2] setPurchaseSharePrice:45.10];
        [[stockList objectAtIndex:2] setCurrentSharePrice:49.51];
        [[stockList objectAtIndex:2] setNumberOfShares:210];
        
        
        // Iterate through the array printing out the value of each
        for (int i = 0; i < 3; i++) {
            NSLog(@"The current value of the stock is %.2f dollars", [[stockList objectAtIndex:i] valueInDollars]); 
        }        
    }
    
    return 0;
}

#5

So really the difference is I created an array of unspecified objects so any object is accepted.

Then is the danger that when I go to use the array there is no way to know that the objects are actually StockHoldings, so I can get unexpected results?

So proper form is to create the array of objects, create the object, then add to array?


#6

Yes, whenever possible avoid losing static type information.

Collection objects are promiscuous, which is both good and bad!

Here is why:

#import <Foundation/Foundation.h>

@interface Foo: NSObject
+ (id)foo;
- (void)printFooness;
@end

@interface FooBar: Foo
+ (id)fooBar;
@end

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        NSArray *objects = [NSArray arrayWithObjects:[Foo foo], [FooBar fooBar], @"Hello World", nil];
        
        for (NSObject *obj in objects)
        {
            if ([obj respondsToSelector:@selector (printFooness)])
                [obj printFooness];
            
            if ([obj respondsToSelector:@selector (lenght)])
                [obj length];
        }
        
        for (Foo *obj in objects)
        {
            [obj printFooness];    // <----------- WILL CRASH HERE
            if ([obj respondsToSelector:@selector (lenght)])
                [obj length];
        }
    }
    return 0;
}

@implementation Foo
+ (id)foo
{
    return [[[self class] alloc] init];
}

- (void)printFooness
{
    NSLog (@"%@", [self className]);
}
@end

@implementation FooBar

+ (id)fooBar
{
    return [[[self class] alloc] init];
}
@end

The CRASH log:

2012-07-11 22:16:31.776 Dangerous[31144:503] Foo
2012-07-11 22:16:31.778 Dangerous[31144:503] FooBar
2012-07-11 22:16:31.778 Dangerous[31144:503] Foo
2012-07-11 22:16:31.779 Dangerous[31144:503] FooBar
2012-07-11 22:16:31.779 Dangerous[31144:503] -[__NSCFConstantString printFooness]: unrecognized selector sent to instance 0x10cfb1358
2012-07-11 22:16:31.780 Dangerous[31144:503] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFConstantString printFooness]: unrecognized selector sent to instance 0x10cfb1358'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff96b76f56 __exceptionPreprocess + 198
	1   libobjc.A.dylib                     0x00007fff8f0fad5e objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff96c031be -[NSObject doesNotRecognizeSelector:] + 190
	3   CoreFoundation                      0x00007fff96b63e23 ___forwarding___ + 371
	4   CoreFoundation                      0x00007fff96b63c38 _CF_forwarding_prep_0 + 232
	5   Dangerous                           0x000000010cfb0b93 main + 1043
	6   Dangerous                           0x000000010cfb0774 start + 52
)
terminate called throwing an exception