0

I'm currently teaching myself Objective-C with the Big Nerd Ranch guide (2nd edition) and I have not had a problem up until chapter 21. I checked the book's forum, but it was of no help because most people there are newbies with spaghetti code, and I do not want to adopt bad habits...I am a beginner too, as this is my first major language, but spaghetti code is obvious when you see it.

I have to take my code from a previous chapter and make a tool that creates an instance of a BNRPortfolio class and fills it with stock holdings (must also sum up the portfolio's current value). Then I have to add a symbol property to BNRStockHolding that holds the stock ticker symbol as an NSString.

I am just completely confused as to where to add the code to do this and how to begin. From my research online, this chapter was poorly written and is where most readers have gotten stuck...the authors even agreed that in future editions (if ever because of Swift moving in) they would re-write the entire chapter.

Currently, my code from a previous chapter looks like this:

BNRStockHolding.h

#import <Foundation/Foundation.h>

@interface BNRStockHolding : NSObject

@property (nonatomic) float purchaseSharePrice;
@property (nonatomic) float currentSharePrice;
@property (nonatomic) int numberOfShares;

- (float)costInDollars;
- (float)valueInDollars;

@end

BNRStockholding.m

#import "BNRStockHolding.h"

@implementation BNRStockHolding

- (float)costInDollars
{
    return _purchaseSharePrice * _numberOfShares;
}

- (float)valueInDollars
{
    return _currentSharePrice * _numberOfShares;
}

@end

Next, I had to add foreign stocks with a conversion rate that converted the values and cost into U.S. dollars:

BNRForeignStockHolding.h

#import <Foundation/Foundation.h>
#import "BNRStockHolding.h"

@interface BNRForeignStockHolding : BNRStockHolding

@property (nonatomic) float conversionRate;

@end

BNRForeignStockHolding.m

#import "BNRForeignStockHolding.h"

@implementation BNRForeignStockHolding : BNRStockHolding

- (float)costInDollars
{
    float foreignCostInDollars = super.costInDollars;
    return _foreignCostInDollars * _conversionRate;
}
- (float)valueInDollars
{
    float foreignValueInDollars = super.valueInDollars;
    return _foreignValueInDollars * _conversionRate;
}

@end

My main file: main.m

#import <Foundation/Foundation.h>
#import "BNRForeignStockHolding.h"

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

        BNRStockHolding *stock0 = [[BNRStockHolding alloc]init];
        BNRStockHolding *stock1 = [[BNRStockHolding alloc]init];
        BNRStockHolding *stock2 = [[BNRStockHolding alloc]init];
        BNRForeignStockHolding *stock3 = [[BNRForeignStockHolding alloc]init];

        stock0.purchaseSharePrice=2.30;
        stock0.currentSharePrice=4.50;
        stock0.numberOfShares=40;

        stock1.purchaseSharePrice=12.19;
        stock1.currentSharePrice=10.56;
        stock1.numberOfShares=90;

        stock2.purchaseSharePrice=45.10;
        stock2.currentSharePrice=49.51;
        stock2.numberOfShares=210;

        stock3.purchaseSharePrice=43.05;
        stock3.currentSharePrice=28.31;
        stock3.numberOfShares=15;
        stock3.conversionRate=0.3;

        NSMutableArray *stocks = [NSMutableArray arrayWithObjects:stock0, stock1, stock2, stock3, nil];
        for (BNRForeignStockHolding *s in stocks) {
            float a = s.purchaseSharePrice;
            float b = s.currentSharePrice;
            int c = s.numberOfShares;

             if ([s isMemberOfClass:[BNRForeignStockHolding class]]) {

                float d = s.foreignCostInDollars;
                float e = s.foreignValueInDollars;
                NSLog(@"\n Purchase Price: %.2f\n Current Price: %.2f\n Number of Shares %d\n Cost in Dollars %f\n Value in Dollars %f\n", a, b, c, d, e);
            }
            else {

                float d = s.costInDollars;
                float e = s.valueInDollars;
                NSLog(@"\n Purchase Price: %.2f\n Current Price: %.2f\n Number of Shares %d\n Cost in Dollars %f\n Value in Dollars %f\n", a, b, c, d, e);
            }

            }
    }
    return 0;
}

I tried this:

In BNRStockHolding.h, I created an instance variable:

- (float)currentTotalValue

and in BNRStockHolding.m, I implemented it like this:

-(float)currentTotalValue
{
return _currentTotalValue = _currentSharePrice * _numberOfShares;
}

Then in main.m, I added this to the 'for' function in my stocks array:

float f = s.currentTotalValue;

Then added %.2f@, f to my NSLog print out.

It worked perfectly except for this caveat:

It simply gave me the value of each individual stock without converting the foreign stocks. I somewhat understand why, but the chapter did not explain thoroughly how to attack such a problem, so I am totally lost. I couldn't even attempt adding the stock ticker symbol because I wasn't sure where to implement it.

When adding up the total value of my portfolio by hand, converted foreign values included, the total should be $17,774.895. Keep in mind that these stocks are pretend, as is the conversion rate, etc.

Thank You for any help, it is greatly appreciated!

EDIT TO ADD

Problem solved, I had to rewrite my BNRStock and BNRForeign .h and .m files with overrides for costInDollars and valueInDollars, as well as add an exception for NSLog to print out separate results for each via an [NSString stringWithFormat:] method. Then I added the sum code recommended with a few tweaks and everything worked well. I also added the stock tickers with no problem by defining symbol as a pointer in my .h and .m files and then including them in the [NSString stringWithFormat:] printout command.

Shoutout to danH for noticing that my costInDollars and valueInDollars should have been overridden to work for both BNRStock and BNRForeign classes, as that was the main issue with my program.

Yistorian
  • 49
  • 10

2 Answers2

0

Not familiar with the book, but it sounds like BNRPortfolio is a new class, deserving of a new .h and .m file. You should crib naming conventions from the work you've done so far. A start would look something like this:

// in BNRPortfolio.h

@class BNRStockHolding;

@interface BNRPortfolio : NSObject

- (void)addHolding:(BNRStockHolding *)holding;
- (float)portfolioValue;

@end

// in BNRPortfolio.m

#import "BNRPortfolio.h"
#import "BNRStockHolding.h"

@interface BNRPortfolio ()  // fixed typo here
@property (strong, nonatomic) NSMutableArray holdings;
@end

@implementation BNRPortfolio

- (id)init {
    self = [super init];
    if (self) {
        _holdings = [NSMutableArray array];
    }
    return self;
}

- (void)addHolding:(BNRStockHolding *)holding {
    [self.holdings addObject:holding];
}

- (float)portfolioValue {
    float sum = 0;
    for (BNRStockHolding *holding in self.holdings) {
        sum += [holding valueInDollars];
    }
    return sum;
}

@end

You should be able to alloc-init one of these, then add several holdings, foreign and domestic, and then ask for the portfolio value.

EDIT - call it from main this way:

#import <Foundation/Foundation.h>
#import "BNRStockHolding.h"
#import "BNRForeignStockHolding.h"
#import "BNRPortfolio.h"

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

    // build stock0, stock1, stock2, stock3 just as you have them, then...

    BNRPortfolio *portfolio = [[BNRPortfolio alloc] init];

    [portfolio addHolding:stock0];
    [portfolio addHolding:stock1];
    [portfolio addHolding:stock2];
    [portfolio addHolding:stock3];

    NSLog(@"The total value of the portfolio in USD is %f", [portfolio portfolioValue]);
    }
return 0;

}

danh
  • 62,181
  • 10
  • 95
  • 136
  • Thank You! I will try this very soon and get back to you. I figured I may have to create a new class, but wasn't sure. Thanks again, I appreciate it! – Yistorian Feb 15 '15 at 02:46
  • I added another related question above, still working with the example that you provided. I think there is some weird bug in my header and implementation files, but I'm unsure what. – Yistorian Feb 16 '15 at 09:35
  • I don't think you should add anything to the header. The problem you're having is with the calling code. It shouldn't be trying to call foreignValueInDollars. There's no such thing. The point of the exercise is that the foreign and domestic stock do the same thing for you, which is tell you their dollar value, even though one uses a more complex equation, the subclass hides that for you. – danh Feb 16 '15 at 15:55
  • Please notice how the code I suggested calls only valueOnDollars, and mentions only the superclass in the code. We want it to be ignorant of the the type of stock. I think your main code should do the same, without asking isMemberOfClass. – danh Feb 16 '15 at 15:58
  • Thx for the advice. I used isMemberofClass because I have to use two separate calculations for the stocks and then sum them up together. I'm afraid that if I use, solely, your main file, I will lose the separate calculations. – Yistorian Feb 16 '15 at 21:39
  • @iProgram - Nope. That's the point of subclassing (polymorphism to be more precise). If your array of holdings is full of foreign and domestic stocks, and each kind implements its own valueInDollars calculation, you'll get the right one when you call the valueInDollars method. I think its this very idea that the tutorial is trying to teach. – danh Feb 16 '15 at 22:51
  • See that's what I dislike about this book. It stated just that, but then asks that the reader create two separate valueInDollars, one for domestic and one for foreign. I'm going to merge the two to make it simpler as discussed because I do agree that currently, the code is overly verbose for no reason, but the book asks for it this way. I guess the book wants the reader to understand how it's broken down, but it should teach best practices. – Yistorian Feb 17 '15 at 01:35
  • I think your code in BNRStockHolding and BNRForeignStockHolding is perfectly sensible. The summing code in the class I suggested should work, too, with no additional code (though I see a typo, will fix in a sec). You should be able to replace that loop with a one liner call to the portfolio class. (Will demonstrate that in edit, too). – danh Feb 17 '15 at 02:14
0

I may just be dumb, but I don't see in your code where you're totaling the entire portfolio value. I see where each stock's total value is calculated. Maybe you need outside the for loop:

float f = 0.0;

then inside the for loop:

f += e;

Here's also just a couple of thoughts on maybe a different approach.

I don't think I'd put any code in main.m. I'd leave it as:

#import <Cocoa/Cocoa.h>

int main(int argc, const char * argv[])
{
    return NSApplicationMain(argc, argv);
}

I'd do the work in the Objective C classes, and there are a number of ways to do this, but here's one (leaving out some details):

I'd enable ARC so I don't need to worry about memory management.

In my AppDelegate.h file I might have:

#import <Foundation/Foundation.h>
#import "BNRStockHolding.h"
#import "BNRPortfolio.h"
#import "BNRForeignStockHolding.h

@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>

{
    @public

    BNRStockHolding *bnrStockHolding;
    BNRPortfolio *bnrPortfolio;
    BNRForeignStockHolding *bnrForeignStockHolding; 
}

then in the AppDelegate.m file:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    bnrStockHolding = [[BNRStockHolding alloc] init;
    bnrPortfolio = [[BNRPortfolio alloc] init];
    bnrForeignStockHolding = [[BNRForeignStockHolding alloc] init];

}

Then in another class (lets say BNRPortfolio) in the .h file, I might have:

@class AppDelegate;

@interface BNRPortfolio : NSObject

{

    @public

    NSMutableArray *holdings;

    @private

    AppDelegate *appDelegate;
}

- (void)addHolding:(BNRStockHolding *)holding;
- (float)portfolioValue;

And in the BNRPortfolio.m file:

#import "BNRPortfolio.h"
#import "AppDelegate.h"

@implementation BNRPortfolio

- (id)init 
{
    self = [super init];
    if (self) 
    {
        holdings = [[NSMutableArray alloc]init];
        appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
    }
    return self;
}

If you set up the variables and an AppDelegate pointer in your other classes, then in those classes, for example, the portfolioValue method could be accessed like:

float portValue = [appDelegate->brnPortfolio portfolioValue];

and holdings could be accessed like:

[appDelegate->brnPortfolio->holdings addObject:holding];

This is obviously just a skeleton of the application (but it's already too long), but I hope you get the idea. In programming there are always many ways to get things accomplished, and sometimes one approach is better than another depending on the circumstances. I just happen to rarely use main.m.

One last comment is that the reason we say @class AppDelegate in the class .h files and import AppDelegate.h in the .m files is that to do otherwise would set up a circular reference, which the compiler will not allow.

That is AppDelegate.h is importing BNRPortfolio.h and BNRPortfolio.h would be importing AppDelegate.h. That's a circular reference.

So we use the @class statement to tell the compiler not to worry for now, we'll define the class later, and then we import AppDelegate.h in the class .m files.

jwlaughton
  • 905
  • 1
  • 6
  • 11
  • I realized that, yes, I was circle referencing everything. I kept the #imports in the header files (BNRPort to BNRForeign to BNRStock), but I added @class BNRPortfolio to BNRStockHoldings.h so that it is aware of the subclasses. This resolved my header and implementation file errors. I was also able to remove the two "@property" lines from BNRForeignStockHoldings.h and that error went away, as well. Still determining the best way to sum up the portfolios and add stock tickers (which is the easier part). – Yistorian Feb 16 '15 at 21:41