Almost got it except for fast enumeration question


#1

I am able to calculate the value of my portfolio, however I was expecting to be able to enumerate through my stocks and provide the number of shares, the value of each share, and the total value of each stock. I placed an enumeration in my Main however I receive a warning "Collection expression type ‘Portfolio’ may not respond to ‘countByEnumeratingWithState:objects:count’.

[code] int i = 1;

    for (ForeignStockHolding *s in thisPortfolio){
        NSLog(@"Stock %d, %d shares at $%.2f are valued at $%.2f", i, [s numberOfShares], [s currentSharePrice], [s valueInDollars]);
        i++;
    }[/code]

When I go to run my project I receive an uncaught exception; "‘NSInvalidArgumentException’, reason: ‘-[Portfolio countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x1001022d0’. What am I doing wrong?

//
//  main.m
//  Stocks
//

#import <Foundation/Foundation.h>
#import "ForeignStockHolding.h"
#import "Portfolio.h"

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

    @autoreleasepool {
        
        Portfolio *thisPortfolio = [[Portfolio alloc]init];
        
        ForeignStockHolding *stock1 = [[ForeignStockHolding alloc]init];
        ForeignStockHolding *stock2 = [[ForeignStockHolding alloc]init];
        ForeignStockHolding *stock3 = [[ForeignStockHolding alloc]init];
        ForeignStockHolding *stock4 = [[ForeignStockHolding alloc]init];
        
        // Details for stock 1
        [stock1 setPurchaseSharePrice:2.30];
        [stock1 setCurrentSharePrice:4.50];
        [stock1 setNumberOfShares:40];
        [stock1 setConversionRate:1.0];
        [thisPortfolio addStockHoldingObject:stock1];
        NSLog(@"%.2f",[stock1 valueInDollars]);
        
        // Details for stock 2
        [stock2 setPurchaseSharePrice:12.19];
        [stock2 setCurrentSharePrice:10.56];
        [stock2 setNumberOfShares:90];
        [stock2 setConversionRate:1.0];
        [thisPortfolio addStockHoldingObject:stock2];
        NSLog(@"%.2f",[stock2 valueInDollars]);
        
        // Details for stock 3
        [stock3 setPurchaseSharePrice:45.10];
        [stock3 setCurrentSharePrice:49.51];
        [stock3 setNumberOfShares:210];
        [stock3 setConversionRate:1.0];
        [thisPortfolio addStockHoldingObject:stock3];
        NSLog(@"%.2f",[stock3 valueInDollars]);
        
        // Details for stock 4
        [stock4 setPurchaseSharePrice:2.30];
        [stock4 setCurrentSharePrice:4.50];
        [stock4 setNumberOfShares:40];
        [stock4 setConversionRate:0.94];
        [thisPortfolio addStockHoldingObject:stock4];
        NSLog(@"%.2f",[stock4 valueInDollars]);
        
        int i = 1;
        
        for (ForeignStockHolding *s in thisPortfolio){
            NSLog(@"Stock %d, %d shares at $%.2f are valued at $%.2f", i, [s numberOfShares], [s currentSharePrice], [s valueInDollars]);
            i++;
        }
        
        NSLog(@"Your portfolio is worth $%@ USD", thisPortfolio);

    }
    return 0;
}
//
//  Portfolio.m
//  Stocks
//

#import "Portfolio.h"
#import "ForeignStockHolding.h"

@implementation Portfolio

@synthesize portfolioID;

- (void)addStockHoldingObject:(ForeignStockHolding *)s
{
    if (!stocks) {
        stocks = [[NSMutableArray alloc]init];
    }
    [stocks addObject:s];
}

- (unsigned int)valueOfPortfolio
{
    unsigned int sum = 0;
    for (ForeignStockHolding *s in stocks) {
        sum += [s valueInDollars];
    }
    return sum;
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"%d", [self valueOfPortfolio]];
}

@end

#2

I think I may understand why I can’t enumerate the way I thought I could. In order for me to enumerate through my array to get a description of each stock would I need to create a method in the class to do it and then call it in Main?


#3

Yes.

You can’t enumerate a Portfolio object because it is not a collection object.
However, you can enumerate its internal array object (stocks):

for (ForeignStockHolding *s in [thisPortfolio stocks]){
...
}
...
@implementation Portfolio
...
- (NSArray *)stocks
{
   return stocks;
}
...
@end

Hint: To directly enumerate a Portfolio object requires adding more code to Portfolio class so that its instances behave like an array object.


#4

Thank you for replying! I thought about this for a day and then it hit me what I was doing wrong. This is my second stab at the book and things are finally starting to make sense! :wink: