0

I am quite confused on doing fast enumeration with array filled with similar objects

Suppose :

I have 1 class (Stock Class), it has 1 subClass ForeignStock.

Properties of Stock Class :

@property float purchaseSharePrice,currentSharePrice;
@property int numberOfShares;

Properties of ForeignStock

@property float conversionRate;

I put instances from those 2 above into a MutableArray. How do I display(NSLog) those 2 different Objects with Fast Enumeration?

 int i = 1;
    for (StockHolding *iterate in shelf) {

        if ([iterate isMemberOfClass:[StockHolding class]])
        {

            NSLog(@"============ Properties of Stock Value %i ============",i);
            NSLog(@"purchase price is  %f",iterate.purchaseSharePrice);
            NSLog(@"current price is %f",iterate.currentSharePrice);
            NSLog(@"number of shares bought is %i",iterate.numberOfShares);

            NSLog(@"--------------Total Value & Cost--------------");
            NSLog(@"Value in dollar for this stock is %f",iterate.valueInDollars);
            NSLog(@"Cost in dollar for this stock is %f",iterate.costInDollars);


            i++;
        }
        else{
            for (ForeignStockHolding *iterate1 in shelf) {
                if ([iterate1 isMemberOfClass:[ForeignStockHolding class]]) {
                    NSLog(@"============ Properties of Stock Value %i  ============",i);
                    NSLog(@"purchase price is  %f",iterate1.purchaseSharePrice);
                    NSLog(@"current price is %f",iterate1.currentSharePrice);
                    NSLog(@"number of shares bought is %i",iterate1.numberOfShares);

                    NSLog(@"--------------Total Value, Cost, & Conversion Rate --------------");
                    NSLog(@"Value in dollar for this stock is %f",iterate1.valueInDollars);
                    NSLog(@"Cost in dollar for this stock is %f",iterate1.costInDollars);
                    NSLog(@"Conversion rate for this stock is %f",iterate1.conversionRate);

                    i++;
                }
            }
        }
    }

Those code above didn't work out, the output for ForeignStock NSLogged 2 times for each ForeignStock instance (I know, for method at second fast enumeration is wrong).

How do I build fast enumeration which it could differ each object's class inside array with different treatment for each class-subclass objects?

  • Your question talks about the `Stock` and `ForeignStock` classes but your code references `StockHolding` and `ForeignStockHolding`. It would help if you made them match up. – rmaddy Aug 03 '14 at 00:22
  • 1
    You can do it with `isKindOfClass` but the better approach would be to put a method into both the parent and the subclass that does whatever you need and simply invoke it in your loop...let the object figure out the right behavior. Polymorphism...it works! :) – Phillip Mills Aug 03 '14 at 00:25
  • Poor design. Make `ForeignStockHolding` a subclass of `StockHolding`. Implement a `-printInfo` method (or similar). `StockHolding` shows as above, `ForeignStockHolding` call `[super printInfo]`, then prints the conversion rate. In general you never want to implement complex knowledge of classes into other classes, that is not what object oriented programming is about. – Gerd K Aug 03 '14 at 00:28

3 Answers3

1

Here's one approach:

int i = 1;
for (StockHolding *iterate in shelf) {

    NSLog(@"============ Properties of Stock Value %i ============",i);
    NSLog(@"purchase price is  %f",iterate.purchaseSharePrice);
    NSLog(@"current price is %f",iterate.currentSharePrice);
    NSLog(@"number of shares bought is %i",iterate.numberOfShares);

    if ([iterate isKindOfClass:[ForeignStockHolding class]])
        NSLog(@"--------------Total Value, Cost, & Conversion Rate --------------");
    else
        NSLog(@"--------------Total Value & Cost--------------");

    NSLog(@"Value in dollar for this stock is %f",iterate.valueInDollars);
    NSLog(@"Cost in dollar for this stock is %f",iterate.costInDollars);

    if ([iterate isKindOfClass:[ForeignStockHolding class]])
        NSLog(@"Conversion rate for this stock is %f", ((ForeignStockHolding*)iterate).conversionRate);

    i++;
}

You don't want a second for loop, you just want to tweak the behavior based on the class. When you test the class, I recommend that you test for the object being an instance of the specialized subclass rather than being it being of exactly the base class.

However, testing for the class of an object is often a code smell. It is usually better to have a class define its own behavior and then have client code simply ask the object to perform the behavior. So, for example, the object could be asked to describe itself. The StockHolding class would generate a description string including its properties. The ForeignStockHolding class would call super to get the base description and then add to it using the properties which it adds.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
1

Deciding what to do based on the result of the isMemberOfClass: call is usually a good indication that you have missed an opportunity for inheritance: in most cases, a proper solution is to add a method to the base class, and override it in the derived class.

In this specific case, consider overriding an existing method called description, inherited from NSObject. Both StockHolding and ForeignStockHolding should provide an implementation:

// This implementation goes in StockHolding
-(NSString*)description {
    return [NSString stringWithFormat:@"purchase price is  %f\n"
        "current price is %f\n"
        "number of shares bought is %i\n"
        "--------------Total Value & Cost--------------\n"
        "Value in dollar for this stock is %f\n"
        "Cost in dollar for this stock is %f"
    ,   _purchaseSharePrice
    ,   _currentSharePrice
    ,   _numberOfShares
    ,   _valueInDollars
    ,   _costInDollars
    ];
}
// This implementation goes into ForeignStockHolding
-(NSString*)description {
    return [NSString stringWithFormat:@"%@\n"
        "Conversion rate for this stock is %f"
    ,   [super description]
    ,   _ conversionRate
    ];
}

With these two implementations in place, you can log all your data uniformly, like this:

for (StockHolding *item in shelf) {
    NSLog(@"%@", item);
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • You might want to rename -display to -description. It's mentioned correctly in your answer but not in the code snippet. – Anurag Aug 03 '14 at 00:27
  • @Anurag You're right, I talk about overriding `description` in the body, and then call it `display` in the example! Thanks! – Sergey Kalinichenko Aug 03 '14 at 00:29
1

If the problem you're trying to solve it to get a good representation of an object for debugging, then consider overriding the -description method in your class. Here's an example of that for both these classes:

@implementation StockHolding

- (NSString *)description
{
    NSString *objectDescriptionFormat = 
    @"============ Properties of Stock Value %@ ============\n"
    "purchase price is  %f\n"
    "current price is %f\n"
    "number of shares bought is %i\n"
    "\n"
    "--------------Total Value & Cost--------------\n"
    "Value in dollar for this stock is %f\n"
    "Cost in dollar for this stock is %f\n";

    return [NSString stringWithFormat:objectDescriptionFormat, 
        [super description],
        self.purchaseSharePrice,
        self.currentSharePrice,
        self.numberOfShares,
        self.valueInDollars,
        self.costInDollars];
}

@end


@implementation ForeignStockHolding

- (NSString *)description
{
    NSString *objectDescriptionFormat = 
    @"%@"
    "Conversion rate for this stock is %f\n";

    return [NSString stringWithFormat:objectDescriptionFormat, 
        [super description],
        self.conversionRate];
}

@end

Logging the object becomes easy because you can just log the object and the description will get printed on the console.

for (StockHolding *stockHolding in shelf)
{
    NSLog(@"%@", stockHolding);
}
Anurag
  • 140,337
  • 36
  • 221
  • 257
  • [super description] on your code is displaying object's address, is it possible to make it to display object's name? for instance I create instance of StockHolding, called StockHolding1, StockHolding2,... then it will display StockHolding1, StockHolding2, not its address. – user3213703 Aug 03 '14 at 02:50
  • Is StockHolding1 the name of the variable, or a property of the object? – Anurag Aug 04 '14 at 19:24