Iteration Question with Subclass


#1

If I have a class called ForeignStockHolding that is a subclass of StockHolding, how do I correctly iterate through the array in the challenge exercise? Is it:

for (Stockholding *s in stocks)

or is it

for (ForeignStockHolding *s in stocks)

I ask because i get the same results either way. I figure ForeignStockHolding is accurate because it inherits everything from StockHolding but I can’t explain why I get the same results for both. Which is accurate and why? Thank you.


#2

You get the same results because deep down, s in for (Stockholding *s in stocks) is pointing to an instance of ForeignStockHolding in stocks even though s’s type (more accurately its static type, the type you see in code) is StockHolding; and also because ForeignStockHolding has (most likely) overridden the method, invoked in the body of the for loop, of Stockholding.

Take a look at this example:

//  main.m

#import <Foundation/Foundation.h>

#define MY_log_prefix() __PRETTY_FUNCTION__
#define MY_log() NSLog (@"%s", MY_log_prefix());
#define MY_logs(P1) NSLog (@"%s: %s", MY_log_prefix(), P1);
#define MY_logS(P1) NSLog (@"%s: %@", MY_log_prefix(), P1);
#define MY_log1(P1, P2) NSLog (@"%s: "P1, MY_log_prefix(), P2);
#define MY_log2(P1, P2, P3, P4) NSLog (@"%s: "P1 P3, MY_log_prefix(), P2, P4);

@interface Bar: NSObject
- (void)whoIsYourSuper;
@end

@interface FooBar: Bar
- (void)whatIsYourName;
@end

int main (int argc, const char * argv[])
{       
    MY_logs ("Create a FooBar...");
    Bar *fooBar = [[FooBar alloc] init];  // Note fooBar's static type: Bar not FooBar
    [fooBar whoIsYourSuper];
    [fooBar performSelector:@selector (whatIsYourName)];
    
    MY_log ();
    @try
    {
        MY_logs ("Create a Bar...");
        Bar *bar = [[Bar alloc] init];
        [bar whoIsYourSuper];
        [bar performSelector:@selector (whatIsYourName)];
    }
    @catch (NSException *x)
    {
        MY_logS ([x description]);
    }
    fflush (NULL);
    return 0;
}

@implementation Bar

- (void)whoIsYourSuper
{
    MY_log2 ("%@", [self className], "'s super is %@", [[self superclass] description]);
}

- (void)doesNotRecognizeSelector:(SEL)selector
{
    MY_logS (NSStringFromSelector (selector));
    [super doesNotRecognizeSelector];
}

@end

@implementation FooBar

- (void)whatIsYourName
{
    // just log the name of the class
    MY_logS ([self className]);
}
@end

Look at the output:

2012-06-30 21:17:08.365 FooBar[1183:503] int main(int, const char **): Create a FooBar...
2012-06-30 21:17:08.367 FooBar[1183:503] -[Bar whoIsYourSuper]: FooBar's super is Bar
2012-06-30 21:17:08.368 FooBar[1183:503] -[FooBar whatIsYourName]: FooBar
2012-06-30 21:17:08.368 FooBar[1183:503] int main(int, const char **)
2012-06-30 21:17:08.369 FooBar[1183:503] int main(int, const char **): Create a Bar...
2012-06-30 21:17:08.369 FooBar[1183:503] -[Bar whoIsYourSuper]: Bar's super is NSObject
2012-06-30 21:17:08.369 FooBar[1183:503] -[Bar doesNotRecognizeSelector:]: whatIsYourName
2012-06-30 21:17:08.370 FooBar[1183:503] -[Bar whatIsYourName]: unrecognized selector sent to instance 0x7f922a8016d0
2012-06-30 21:17:08.371 FooBar[1183:503] int main(int, const char **): -[Bar whatIsYourName]: unrecognized selector sent to instance 0x7f922a8016d0

And make sure you understand what’s going on with method invocations on instances of Bar and FooBar.


#3

Usually when I talk about subclasses, I talk about the idea of inheriting capabilities. For example, the ForeignStockHolding inherits all the capabilities of StockHolding, but extends those capabilities to include a currency conversion.

But subclasses is also about type: A ForeignStockHolding is a type of StockHolding.

So, if you know the array contains an assortment of instances of ForeignStockHolding, StockHolding, and PrivateStockingHolding, you know they are all types of StockHolding, so you would use that as the type:

for (StockHolding *s in stocks) {
But, if you are certain that everything in the array is a ForeignStockHolding, and, in particular, you are going to use one of the capabilities specific to ForeignStockHolding, you will use that as the type:

float sumOfConversionRates = 0; for (ForeignStockHolding *s in stocks) { sumOfConversionRates = [s currencyRate]; } float avgConversionRage = sumOfConversionRates / [stocks count];
I hope that is useful.