Trying to create a portfolio of stocks with a tota

2019-09-16 11:57发布

问题:

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.

回答1:

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;

}



回答2:

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.