Setting up a class identifier to be called when logging array data - objective-c

Hey stackOverflow community. I am working through Big Nerd Ranch's Objective C book and have come across the fun chapter on defining and setting up classes (chapter 17 if you're familiar). In it the challenge has us write a program where we define a stock class with several properties and instance variables. I have been able to get the program to work as asked but I want to tinker a little with it to get it to also NSLog a stockName so I can see what stock is associated with its properties.
Basically, is there a way to make the code more concise for this block:
NSString *appleName = #"AppleInc";
[Apple setStockIdentifier:appleName];
Maybe more like this:
[Apple setStockIdentifier:"AppleInc"];
I tried setting the property as a char in the class file but couldnt get it to work. Im new to this but I'm thinking that declaring a new NSString for the stockIdentifier value is extra code that isn't needed. Any feedback would be greatly appreciated.
Below is what I have for the main file:
#import <Foundation/Foundation.h>
#import "StockHolding.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
StockHolding *Apple = [[StockHolding alloc] init];
NSString *appleName = #"AppleInc";
[Apple setStockIdentifier:appleName];
[Apple setPurchaseSharePrice:2.30];
[Apple setCurrentSharePrice:4.50];
[Apple setNumberOfShares:40];
StockHolding *HomeDepot = [[StockHolding alloc] init];
NSString *homeDepotName = #"Home Depot Inc";
[HomeDepot setStockIdentifier:homeDepotName];
[HomeDepot setPurchaseSharePrice:12.19];
[HomeDepot setCurrentSharePrice:10.56];
[HomeDepot setNumberOfShares:90];
StockHolding *Cisco = [[StockHolding alloc] init];
NSString *ciscoName = #"Cisco Inc";
[Cisco setStockIdentifier:ciscoName];
[Cisco setPurchaseSharePrice:45.10];
[Cisco setCurrentSharePrice:49.51];
[Cisco setNumberOfShares:210];
NSMutableArray *listOfStocks = [NSMutableArray arrayWithObjects:Apple, HomeDepot, Cisco, nil];
for (StockHolding *currentStock in listOfStocks) {
NSLog(#"%#, Purchase Share Price: %.2f; Current value: %.2f; Number of shares: %i",[currentStock stockIdentifier],[currentStock purchaseSharePrice], [currentStock currentSharePrice], [currentStock numberOfShares]);
}
}
return 0;
}
Below is the contents of StockHolding.h:
#import <Foundation/Foundation.h>
#interface StockHolding : NSObject
{
//char stockIdentifier;
float purchaseSharePrice;
float currentSharePrice;
int numberOfShares;
}
#property NSString *stockIdentifier;
#property float purchaseSharePrice;
#property float currentSharePrice;
#property int numberOfShares;
-(float) costInDollars; //purchaseSharePrice * numberOfShares;
-(float) valueInDollars; //currentSharePrice * numberOfShares;
#end
And here is StockHolding.m:
#import "StockHolding.h"
#implementation StockHolding
#synthesize currentSharePrice, purchaseSharePrice, numberOfShares, stockIdentifier;
-(float)costInDollars;
{
return (purchaseSharePrice * numberOfShares);
}
-(float)valueInDollars;
{
return (currentSharePrice * numberOfShares);
}
#end

This is simply a syntax error.
[Apple setStockIdentifier:"AppleInc"];
Should be...
[Apple setStockIdentifier:#"AppleInc"];

Related

How to input new instance of Object with multiple arguments into NSMutablearray

I'm very new to Obj-C, been learning more Java and C++ lately.
I have two objects Friend and Foe which inherit the Character Object. Friend and Foe have slightly different attributes. I want all Friends and Foes to be in the same NSMutablearray. Can't figure out how to put these into the array. I get an error saying too many arguments, expected 1 have 4. For Foe its the same, but expected 1, have 5.
The Character Object
#import <foundation/foundation.h>
#interface Character : NSObject
#property NSString *name;
#property NSInteger strength;
#property NSInteger iff;
- (void) printDetails;
#end
#import <Foundation/Foundation.h>
#import "game_character.h"
#implementation Character
- (void) printDetails
{
NSLog (#"%# has strength %ld\n", self.name, self.strength);
}
#end
The Friend Object (The Foe object is similar with without intelligence and spell but has an alternate NSInteger attribute.
#interface Friend : Character
#property NSInteger intelligence;
#property NSString *spell;
- (void)printDetails;
#end
#import <Foundation/Foundation.h>
#import "game_character.h"
#import "friend.h"
#implementation Friend
-(void)printDetails
{
NSLog (#"%# has strength %ld\n", self.name, self.strength);
NSLog (#" ,Intelligence %ld, Spell %#\n", self.intelligence, self.spell);
}
#end
The Friend Input Method (I will have a similar method to input a Foe)
void input_friend()
{
#autoreleasepool
{
char str[30] = {0};
NSInteger strength;
NSInteger iff=1;
NSInteger intelligence;
NSLog(#"Enter character name\n");
scanf("%s", str);
NSString *name = [NSString stringWithUTF8String:str];
NSLog(#"Enter character strength\n");
scanf("%ld", &strength);
NSLog(#"Enter character intelligence");
scanf("%ld", &intelligence);
NSLog(#"Enter character spell\n");
scanf("%s", str);
NSString *spell = [NSString stringWithUTF8String:str];
My Error is here when I try to add the object to the array.
[characters addObject:name, strength, iff, intelligence, spell];
}
}
The Main so far. I intend to add a menu with option to add Friend or Foe to the array.
int main(int argc, const char * argv[])
{
#autoreleasepool
{
characters = [[NSMutableArray alloc] init];
void input_friend();
void input_foe();
}
return 0;
}
In this line, you are passing multiple arguments to add to your list of characters. However, this doesn't work since you want to add objects, which is why you got your error:
[characters addObject:name, strength, iff, intelligence, spell];
So you need to initialize a new Friend or Foe first, set its properties, and then add it to your array.
Friend *newFriend = [[Friend alloc] init];
newFriend.name = name;
newFriend.strength = strength;
// etc.
[characters addObject: newFriend];

Trying to create a portfolio of stocks with a total value in Objective-C, but unsure of the approach

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.
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;
}
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.

any mistakes using #property and #synthesize

I'm a newbie of Objective-C language and I'm trying to understand the basic concepts. I came from Java language so I have already know the principle of OOP programming. Here it is the code I wrote. It's very simple but it doesn't work properly. I have some issues using #property and #synthesize
#import <Foundation/Foundation.h>
#interface Car: NSObject
#property(nonatomic,retain) NSString *brand;
#property int year;
#end //Car Interface
#import "Car.h"
#implementation Car
#synthesize brand;
#synthesize year;
#end //Car Implementation
#import "Car.h"
int main (int argc, const char * argv[])
{
int y;
//Creo un nuovo oggetto
Car *myCar = [[Car alloc] init];
//Setto i parametri
[myCar setbrand: #"BMW Z4"];
NSLog (#"Inserisci data modello: ");
scanf (" %i", &y); //E' buona norma lasciare uno spazio
[myCar setyear: y];
//Stampo a video i dati
NSLog(#"Marca: %# Anno: %i", [myCar setbrand], [myCar setyear]);
}
I don't know where it's the error. I'm pretty sure there is some mistakes in main function. is it correct call that methods in that way?
The setters must be capitalized properly.
[myCar setBrand:#"BMW Z4"];
[myCar setYear:2010];
The getters default to the property name.
NSString *carBrand = [myCar brand];
int carYear = [myCar year];

What's wrong with my Objective-C class?

I am having trouble with my Objective-C code. I am trying to print out all of the details of my object created from my "Person" class, but the first and last names are not coming through in the NSLog method. They are replaced by spaces.
Person.h: http://pastebin.com/mzWurkUL
Person.m: http://pastebin.com/JNSi39aw
This is my main source file:
#import <Foundation/Foundation.h>
#import "Person.h"
int main (int argc, const char * argv[])
{
Person *bobby = [[Person alloc] init];
[bobby setFirstName:#"Bobby"];
[bobby setLastName:#"Flay"];
[bobby setAge:34];
[bobby setWeight:169];
NSLog(#"%s %s is %d years old and weighs %d pounds.",
[bobby first_name],
[bobby last_name],
[bobby age],
[bobby weight]);
return 0;
}
%s is for C style strings (a sequence of chars terminated by a null).
Use %# for NSString objects. In general, %# will invoke the description instance method of any Objective C object. In the case of NSString, this is the string itself.
See String Format Specifiers.
On an unrelated note, you should look into Declared Properties and #synthesize for your class implementation. It will save you a lot of typing as it produces all the getters and setters for you:
person.h:
#import <Cocoa/Cocoa.h>
#interface Person : NSObject
#property (nonatomic, copy) NSString *first_name, *last_name;
#property (nonatomic, strong) NSNumber *age, *weight;
#end
person.m
#import "Person.h"
#implementation Person
#synthesize first_name = _first_name, last_name = _last_name;
#synthesize age = _age, weight = _weight;
#end
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
int main (int argc, const char * argv[])
{
Person *bobby = [[Person alloc] init];
bobby.first_name = #"Bobby";
bobby.last_name = #"Flay";
bobby.age = [NSNumber numberWithInt:34]; // older Objective C compilers.
// New-ish llvm feature, see http://clang.llvm.org/docs/ObjectiveCLiterals.html
// bobby.age = #34;
bobby.weight = [NSNumber numberWithInt:164];
NSLog(#"%# %# is %# years old and weighs %# pounds.",
bobby.first_name, bobby.last_name,
bobby.age, bobby.weight);
return 0;
}

No print output in Xcode

I have a program which runs fine, but I get no print output even though I have a NSLog file in main.m Can you tell me what's wrong? Thank you.
main.m
#import <Foundation/Foundation.h>
#import "Stockholding.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
StockHolding *stockA;
StockHolding *stockB;
StockHolding *stockC;
[stockA setPurchaseSharePrice:2.40];
[stockA setCurrentSharePrice:3.12];
[stockA setNumberOfShares:40];
[stockB setPurchaseSharePrice:1.50];
[stockB setCurrentSharePrice:1.41];
[stockC setNumberOfShares:35];
[stockC setPurchaseSharePrice:1.10];
[stockC setCurrentSharePrice:1.20];
[stockC setNumberOfShares:60];
NSArray *holdings = [NSArray arrayWithObjects:stockA, stockB, stockC, nil];
for (StockHolding *n in holdings) {
// Call the methods
float cost = [n costInDollars];
float value = [n valueInDollars];
NSLog(#"Bought stock for $%.2f, It is now at $%.2f, I have %d shares, They cost me $%.2f, Now they are worth $%.2f", [n purchaseSharePrice], [n currentSharePrice], [n numberOfShares], cost, value);
}
}
return 0;
}
StockHolding.h
#import <Foundation/Foundation.h>
#interface StockHolding : NSObject {
float purchaseSharePrice;
float currentSharePrice;
int numberOfShares;
}
#property float purchaseSharePrice;
#property float currentSharePrice;
#property int numberOfShares;
-(float)costInDollars;
-(float)valueInDollars;
#end
StockHolding.m
#import "StockHolding.h"
#implementation StockHolding
#synthesize purchaseSharePrice, currentSharePrice, numberOfShares;
-(float)costInDollars
{
return (purchaseSharePrice * numberOfShares);
}
-(float)valueInDollars
{
return (currentSharePrice * numberOfShares);
}
#end
You haven't actually created any of those StockHolding objects. Thus, your array is empty, and the loop doesn't do anything.
StockHolding *stockA;
is just a declaration of a pointer. You need to create the object to which it points; the usual procedure is this:
StockHolding *stockA = [[StockHolding alloc] init];
Since, under ARC, object pointers are initialized to nil (which means "no object"), you are passing nil as all of the arguments to arrayWithObjects:. nil being the sentinel value meaning "there are no more arguments", the array is created without contents.
With an empty array, for (StockHolding *n in holdings) doesn't have anything to enumerate over, so none of the code in the body of the loop, including your NSLog(), gets executed.