I just started out programming in Objective C. This is very simple stuff but im wondering what im missing on my knowledge of properties, as far as I understand this should work but it gives me the error:
Property 'score' not found on object of type 'Player *'
I have a Player class and the code as follows below, each bolded is a separate file
Player.h has:
#import <Foundation/Foundation.h>
#interface Player : NSObject
#property int score;
#end
Player.m has:
#import "Player.h"
#implementation Player
- (id)init {
self = [super init];
if (self){
_score = 5000;
}
return self;
}
#end
main.m has
#import <Foundation/Foundation.h>
#import "Player.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Player *firstPlayer = [[Player alloc] init];
NSLog(#"The default score is %i", [firstPlayer score]);
}
return 0;
}
Player.h is missing the following line which should go between the #import and #property lines:
#interface Player : NSObject
Where NSObject may be a different class, but needs to be whatever you intend to be subclassing.
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.
So I am quite new on OC programming, I come from Front-end background (i.e. HTML/CSS/JavaScript ...), so I understand basic concepts of programming :)
Basically I created a console application, with a simple FooClass.
FooClass.h
#import <Foundation/Foundation.h>
#interface FooClass : NSObject
#property (strong, nonatomic) NSString *username;
- (NSString *) username;
- (void) setUsername:(NSString *)username;
#end
FooClass.m
#import "FooClass.h"
#implementation FooClass
#synthesize username = _username;
- (instancetype) init
{
self = [super init];
if (self) {
}
return self;
}
- (NSString *) username
{
return _username;
}
- (void) setUsername:(NSString *)username
{
_username = username;
}
#end
And in the main.m file, where the app bootstraps.
#import <Foundation/Foundation.h>
#include "FooClass.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
FooClass *foo = [[FooClass alloc] init];
foo.username = #"a";
}
return 0;
}
XCode tells me that it cannot find property username on object of type FooClass. And I don't really have idea about it. Any one could help?
I am a bit late in posting the answer. Here are few things that you should consider.
Since you have a property username. You are not required to create methods for setters and getters. The compiler will create them for you. Simply remove the two statements.
No need to synthesize in .m as well.
Instead of #include use #import. import takes only one copy even if you try to add the file(s) directly or indirectly from other files as compared to include.
I'm having trouble with Objective C.
I'm trying to call a block after I've moved a sprite
The short version of what i'm trying to achieve is that i want to move all of the enemies in an array, and when each one finishes moving i want to check whether it has collided.
The simplified code below shows what i'm trying to do.
My Actor class is defined like this
// -----------------------------
// Actor.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "ScreenObject.h"
typedef void (^actorMoveComplete)(void);
#interface Actor : ScreenObject
{
// actorMoveComplete onMoveComplete;
}
#property (nonatomic) BOOL isMoving;
#property (readwrite, copy) actorMoveComplete onMoveComplete;
#property (nonatomic, retain) CCSprite* sprite;
-(void) moveTo:(CGPoint)targetPosition Speed:(float) speed withCallback:(actorMoveComplete)moveComplete;
#end
// -----------------------------
// Actor.m
#import "Actor.h"
#implementation Actor
#synthesize onMoveComplete;
#synthesize sprite;
-(void) moveTo:(CGPoint)targetPosition Speed:(float) speed withCallback:(actorMoveComplete)moveComplete;
{
onMoveComplete = moveComplete;
id actionMove = [CCMoveTo actionWithDuration:speed position:targetPosition];
id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)];
[super.sprite runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
}
-(void) spriteMoveFinished:(id) sender
{
isMoving = NO;
if (onMoveComplete != nil)
onMoveComplete();
}
#end
As you can see i'm trying to store the block in an onMoveComplete parameter (i've also tried it in a private variable), and then call it once the sprite move has completed.
In my calling class i'm iterating through a bunch of actors (enemies) and i want to call this anonymous code block for each actor once the move has completed:
{
[self checkForCollision:enemy];
}
My calling class looks like this.
//------------------------------
//
// GameLayer.h
#import "cocos2d.h"
#import "Actor.h"
#interface GameLayer : CCLayerColor
{
}
#property (nonatomic, copy) NSMutableArray *enemies;
- (void) updateEnemies;
- (void) checkForCollision:(Actor*)actor;
- (BOOL) isMapGridClear:(CGPoint)mapGrid excludeActor:(Actor*)actor;
#end
//------------------------------
// GameLayer.m
#import "GameLayer.h"
#implementation GameLayer
#synthesize enemies;
- (void) updateEnemies
{
for (Actor *enemy in enemies)
{
//
CGPoint newPosition = [self getNewPosition:enemy]; /* method to figure out new position */
[enemy moveDelta:ccp(dX, dY) Speed:enemySpeed withCallback:^{
[self checkForCollision:enemy];
}];
}
}
- (void) checkForCollision:(Actor*)actor
{
if (![self isMapGridClear:actor.gridPosition excludeActor:actor])
{
actor.isCrashed=YES;
[actor loadSprite:self spriteImage:#"crashed.png"];
}
}
- (BOOL) isMapGridClear:(CGPoint)mapGrid excludeActor:(Actor*)actor
{
/* Actual method figures out if there was a collision */
return YES;
}
#end
Unfortunately when I call the onMoveComplete block, i keep getting an EXC_BAD_ACCESS error
Interestingly, if I try to call the block inside the moveTo method, it works (but of course i want this to trigger AFTER the movement has completed).
Can anyone help me with what i'm doing wrong. Am I even using the correct mechanism?
(Apologies for the poorly formatted, incomplete code segments)
You correctly declared your property as copy, but you are setting your instance variable directly to the address of the block without using the generated accessors. That means the block won't get copied and gets destroyed before it is called.
Assign your block using self.onMoveCompleted = moveCompleted and you will be fine.
I want to have an ivar of type Class, and to keep the pointer around after it is passed in. But no matter what I do, arc will not let me do that. For instance, if I declare
#property (nonatomic, strong) Class myClass;
the compiler decides that myClass should be unsafe_unretained. And if I try this:
-(id) initWithClass: (Class) passedInClass {
if ((self = [super init])) {
self.myClass = passedInClass;
}
return self;
}
what happens is that even if the class is non-nil in the calling code, it is nil within the init method.
Short of turning off arc, is there any way to get around this?
EDIT: This question is just wrong. It does work. See the accepted answer.
Works as advertised with Xcode 4.3.2 targeting 10.7 and 5.1:
#interface MOYNObject : NSObject
#property (nonatomic, strong, readwrite) Class myClass;
#end
#implementation MOYNObject
#synthesize myClass;
- (id)initWithClass:(id)pClass
{
self = [super init];
if (self != nil)
self.myClass = pClass;
assert(self.myClass);
CFShow((__bridge const void*)self.myClass);
return self;
}
#end
int main(int argc, const char* argv[]) {
#autoreleasepool {
MOYNObject * o = [[MOYNObject alloc] initWithClass:[NSString class]];
// ...
}
return 0;
}
Are you ahead of or behind 4.3.2?