Objective - c program help (source included)

Joined
Jun 6, 2008
Messages
27
Reaction score
0
Points
1
Your Mac's Specs
Blackbook - 2.4 GHZ Core 2 Duo - 2 GB RAM - 250 GB HDD
I am just learning objective-c. I have stephen kochan's new book on pre-order (anyone know when that should be out?) and I am just building simple programs for practice.

I made a program that calculates the first 13 numbers of Fibonacci's sequence, but now I am trying to make the program calculate one further number in the sequence per button push and I can't figure out how to do it. Here is the code I have so far. The error I get is that the variables in the else statement are undefined. I know that variables are protected and they can't be accessed from different functions (I guess the else statement makes it a different function?) but how do I get around this problem?

Code:
#import "control.h"

@implementation control
- (IBAction)generate:(id)sender 
{
	
	
	if (buttonPress != 1)
	{
		buttonPress = 1;
		int a = 0;
		int b = 1;
		int c;

		NSLog(@"%d", a);	//print first 
		NSLog(@"%d", b);	//print second
		c = a + b;
		NSLog(@"%d", c);	//print third
	
		int i;
		for (i=0; i<=9; i++) //print #4-#13 of the sequence
		{
			a = b;
			b = c;
			c = a + b;
			NSLog(@"%d", c);

		}
	}
	
	else // print one additional number in the sequence per button press
	{
		a = b;
		b = c;
		c = a + b;
		NSLog(@"%d", c);
	}
	
}

@end

edit - I found that if I initialize the variables in the @interface section it will compile but I get the warning "local declaration hides instance variable" and when you run the program the first button press gives you the first 13 numbers but every press after that gives a zero.
 
OP
J
Joined
Jun 6, 2008
Messages
27
Reaction score
0
Points
1
Your Mac's Specs
Blackbook - 2.4 GHZ Core 2 Duo - 2 GB RAM - 250 GB HDD
I got it working using setters and getters, but the compiler still gives me a bunch of warnings. I know this isn't the best code, but am I at least on the right track? The textbox outlet is there because I would like to eventually have it display the results in the app window rather than the console but for now I am just trying one thing at a time. Here is the working code, sorry for the sloppiness. Originally I setup a new class to handle the setting and getting of the variables, but for some reason it didn't work. Is that the correct way to do it?

Code:
#import <Cocoa/Cocoa.h>

@interface control : NSObject {
    IBOutlet id textBox;
	int a;
	int b;
	int c;
	int buttonPress;
}

- (IBAction)generate:(id)sender;
- (void) seta: (int) newNumber;
- (void) setb: (int) newNumber;
- (int) a;
- (int) b;

@end

#import "control.h"

@implementation control
- (IBAction)generate:(id)sender 
{
	
	if (buttonPress != 1)
	{
		buttonPress = 1;
		int a = 0;
		int b = 1;
		int c;

		NSLog(@"%d", a);	//print first 
		NSLog(@"%d", b);	//print second
		c = a + b;
		NSLog(@"%d", c);	//print third
	
		int i;
		for (i=0; i<=9; i++) //print #4-#13 of the sequence
		{
			a = b;
			b = c;
			c = a + b;
			NSLog(@"%d", c);

		}
		[self seta: a];
		[self setb: b];
		[self setc: c];
	}
	
	else // print one additional number in the sequence per button press
	{
		a = [self a];
		b = [self b];
		c = [self c];
		a = b;
		b = c;
		c = a + b;
		NSLog(@"%d", c);
	}
	
}

- (int) a
{
	return a;
}

- (int) b
{
	return b;
}

- (int) c
{
	return c;
}

- (void) setc: (int) newNumber
{
	c = newNumber;
}


- (void) seta: (int) newNumber
{
	a = newNumber;
}

- (void) setb: (int) newNumber
{
	b = newNumber;
}

@end
 
Joined
Mar 15, 2007
Messages
161
Reaction score
4
Points
18
Your Mac's Specs
17" MacBook Pro, 2.33GHz C2D, 2GB RAM
You've got a lot of issues in this code that I don't have time to dig through, but I will at least tell you that the undefined variable errors in the earlier version of the code were because your declarations of variables a, b and c were scoped to the first part of the "if" statement. (No, the "else" doesn't make it part of a different function.) Since you also wanted to use the same variables in the "else" part, the most likely approach would have been for you to move the declarations to before the "if" statement (so that you would be using the same variables in both places).

In the 2nd version of the code, you've got namespace confusion from using the same variable names (a, b, c) as locals within your "generate" function and also as instance variables of the class object (leading to the "local definition hides instance variable" error you mentioned). This is never a good idea, as can be seen in the "else" portion of your code shown here, where you may think you are assigning values to the locals from the instance variable accessor methods, but in fact you are using the accessor methods to get values for the instance variables and are immediately storing the values right back into the same instance variables they came from (and without using the mutator methods for the latter task).

Beyond these fundamental issues, you're also not really on the right track for making a program that responds to a button press to perform additional work. You may want to wait until you have that book in hand before going any further, so that you don't keep going further down the wrong road and accumulating more stuff that you'll only need to unlearn eventually. Or if you don't want to wait, then find some more online Obj-C tutorials and start going through those.
 
OP
J
Joined
Jun 6, 2008
Messages
27
Reaction score
0
Points
1
Your Mac's Specs
Blackbook - 2.4 GHZ Core 2 Duo - 2 GB RAM - 250 GB HDD
Thank you for you help. Could you quickly tell me how it should be done? I am not asking for code, but just in english. It seems to me that if I just move the variable declarations outside the if statement the first code is very close to working, but then the buttonPress check doesn't work for some reason.
 
Joined
Mar 15, 2007
Messages
161
Reaction score
4
Points
18
Your Mac's Specs
17" MacBook Pro, 2.33GHz C2D, 2GB RAM
Given how you have structured your code, you do need persistent instance values rather than local variables in order to keep around the current sequence data values for calculating subsequent values on subsequent button pushes. Since they are only used internally, you don't really need accessor and mutator methods. I'd define a, b, and c as instance variables (although I'd probably choose more descriptive names) and then use them directly in the Fibonacci calculations, with no automatic (local) variables at all. The value of buttonPress also needs to be persistent, either as an instance variable (as in your 2nd example) or as a static local variable in the generate method.

I would suggest restructuring things to at least move the Fibonacci calculations out of generate and into a separate method (perhaps named something like getNextFibonacciValue) that simply calculates the next value in the sequence. Your generate method would then call getNextFibonacciValue, in order to keep the Fibonacci calculation in exactly one unique place. Duplication of algorithmic code in multiple locations invites problems in any non-trivial program, so it's wise to get used to designing your code that way from the outset. In fact I'd go further and say that the code and persistent values for the Fibonacci algorithm belong in a separate class object that is composited into the controller class, which hides its internal data (a, b, and c) and just provides the next Fibonacci number as a return value from getNextFibonacciValue, since the generate method doesn't really need to know anything more than that.

EDIT: This is short enough that I'll whip up some code to make it clearer, so stop reading now if you want to try it yourself first. Notice that the restructured code is quite concise. I didn't create a separate Fibonacci class for this example, but it would be similar in most respects.

Code:
@interface appController : NSWindowController {
	int prev1;
	int prev2;
	int current;
	int seqVal;	
}
- (IBAction)generate:(id)sender;
- (int)getNextFibonacciValue;
@end

@implementation appController
- (IBAction)generate:(id)sender
{
	static int initialSeqShown = 0;
	if (!initialSeqShown)
	{
		for (int i = 0; i < 13; ++i)
			NSLog(@"%d", [self getNextFibonacciValue]);
		initialSeqShown = 1;
	}
	else
		NSLog(@"%d", [self getNextFibonacciValue]);
}

- (int)getNextFibonacciValue
{
	if (seqVal <= 1)
		current = seqVal++;
	else
	{
		prev2 = prev1;
		prev1 = current;
		current = prev1 + prev2;		
	}
	return current;
}
@end
 
OP
J
Joined
Jun 6, 2008
Messages
27
Reaction score
0
Points
1
Your Mac's Specs
Blackbook - 2.4 GHZ Core 2 Duo - 2 GB RAM - 250 GB HDD
Thanks a lot for your help. This is what I came up with. I changed it a little so that it generates one number in the sequence per button press rather than generating the first 13 with one press.

Code:
#import <Cocoa/Cocoa.h>


@interface control : NSObject {
    IBOutlet id textBox;
	int a;
	int b;
	int c; 
	int press;
}

- (IBAction)generate:(id)sender;
- (int)nextNumber;

@end

#import "control.h"


@implementation control
- (IBAction)generate:(id)sender 
{
	NSLog(@"%d",[self nextNumber]); // print next number in fibonacci sequence
}

- (int)nextNumber
{
	if (!press) // for the first press return 0 because it is the first number in the sequence
	{
		press = 1;
		return 0;
	}
	else if (press == 1) // for the second press return 1 because that is the second number in the sequence
	{
		press = 2;
		return 1;
	}
	else if (press == 2) // for the third press set a and b equal to the first and second number in the sequence
	{
		press = 3;
		a = 0;
		b = 1;
	}
	else // set a and b as the most recently calculated numbers in the sequence
	{
		a = b;
		b = c;
	}
	c = a + b; // determine next value in the sequence by adding the most recent two values
	return c; 
}

@end

I do have a few more questions.

Is it okay to cut the nextNumber method short by returning values in the if statements?

If I was going to create a seperate fibonacci class would I have to initialize and alloc an instance of that class before I use it? Then all the work would be done by a single instance of the class. Is that right?

Rather than using NSLog to display the numbers I would like to output them using the textBox outlet. It is linked to a large text area in my GUI. I know that if I just use setStringValue it sets it to the new value and deletes the old value. Would I use a mutable string to add the new number values to the end of the string as they are created and output that?

Sorry about all the questions. I just finished coding and havn't done any research yet. I will edit my post if I find the answers I am looking for.

I am going to take your advice about doing tutorials for now to get a better understanding rather then trying my own projects.

Thanks again,

Josh
 
Joined
Mar 15, 2007
Messages
161
Reaction score
4
Points
18
Your Mac's Specs
17" MacBook Pro, 2.33GHz C2D, 2GB RAM
I do have a few more questions.

Is it okay to cut the nextNumber method short by returning values in the if statements?

Yes, it's okay, although I generally try to avoid it. I was taught that it is better programming form to have exactly one exit point in any function, and I try to do that whenever possible (you may have noticed I arranged things that way in my implementation of the Fibonacci function). However, it is not at all uncommon to see multiple return statements embedded in nested code within a function in order to gain a slight performance improvement, or to simplify the code that follows.

If I was going to create a seperate fibonacci class would I have to initialize and alloc an instance of that class before I use it? Then all the work would be done by a single instance of the class. Is that right?

It sounds like you are on the right track. Don't forget you'd need a pointer to the class as an instance variable in your controller class, e.g.:

Code:
#import "fibonacciGenerator.h"

@interface appController : NSWindowController {
	fibonacciGenerator *fibGen;
}
...
@implementation appController
- (id)init
{
	[super init];
	fibGen = [[fibonacciGenerator alloc] init];
	return self;
}
@end

Rather than using NSLog to display the numbers I would like to output them using the textBox outlet. It is linked to a large text area in my GUI. I know that if I just use setStringValue it sets it to the new value and deletes the old value. Would I use a mutable string to add the new number values to the end of the string as they are created and output that

If you mean that you'd create an NSMutableString as an instance member of your controller class, and then append the next sequence value to its contents before making each call to setStringValue for the text area, then yes, that would be a reasonable approach.
 
OP
J
Joined
Jun 6, 2008
Messages
27
Reaction score
0
Points
1
Your Mac's Specs
Blackbook - 2.4 GHZ Core 2 Duo - 2 GB RAM - 250 GB HDD
Is there any way to make something like this work?

Code:
[list appendString:(@", %d",[self nextNumber])];

I looked through the documentation and tried a few different things, but I can't get it.

list is defined like this in the @interface section

Code:
NSMutableString *list;
 
Joined
Mar 15, 2007
Messages
161
Reaction score
4
Points
18
Your Mac's Specs
17" MacBook Pro, 2.33GHz C2D, 2GB RAM
Is there any way to make something like this work?

Code:
[list appendString:(@", %d",[self nextNumber])];

Replace "appendString" with "appendFormat" as I've shown below. Note that you also had some extraneous parentheses which I've removed:

Code:
    [list appendFormat:@", %d", [self nextNumber]];
 
OP
J
Joined
Jun 6, 2008
Messages
27
Reaction score
0
Points
1
Your Mac's Specs
Blackbook - 2.4 GHZ Core 2 Duo - 2 GB RAM - 250 GB HDD
I think I may have tried that last night, but when I do the program locks up on the second button click. Here is the code:

Code:
#import <Cocoa/Cocoa.h>


@interface control : NSObject {
    IBOutlet id textBox;
	int a;
	int b;
	int c; 
	int press;
	int firstPress;
	NSMutableString *list;
}

- (IBAction)generate:(id)sender;
- (int)nextNumber;

@end


#import "control.h"


@implementation control
- (IBAction)generate:(id)sender 
{
	if (!firstPress)   // print first number in sequence without comma
	{
		firstPress = 1;
		list = [NSMutableString stringWithFormat:@"%d", [self nextNumber]];
		[textBox setString:list];
	}
	else   // print next number in fibonacci sequence with comma before each number
	{
		[list appendFormat:@", %d",[self nextNumber]];
		[textBox setString:list];
	}
	
}

- (int)nextNumber
{
	if (!press) // for the first press return 0 because it is the first number in the sequence
	{
		press = 1;
		return 0;
	}
	else if (press == 1) // for the second press return 1 because that is the second number in the sequence
	{
		press = 2;
		return 1;
	}
	else if (press == 2) // for the third press set a and b equal to the first and second number in the sequence
	{
		press = 3;
		a = 0;
		b = 1;
	}
	else // set a and b as the most recently calculated numbers in the sequence
	{
		a = b;
		b = c;
	}
	c = a + b; // determine next value in the sequence by adding the most recent two values
	return c; 
}

@end

The debugger loads all this into the window on the second click. I have never seen it before.

Code:
[Session started at 2008-12-18 11:24:27 -0500.]

[Session started at 2008-12-18 11:24:34 -0500.]
Loading program into debugger…
GNU gdb 6.3.50-20050815 (Apple version gdb-768) (Tue Oct  2 04:07:49 UTC 2007)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".Program loaded.
sharedlibrary apply-load-rules all
Attaching to program: `/Users/Josh/My Documents/Apps/Fibonacci/build/Debug/Fibonacci.app/Contents/MacOS/Fibonacci', process 1343.
(gdb)
 
Joined
Mar 15, 2007
Messages
161
Reaction score
4
Points
18
Your Mac's Specs
17" MacBook Pro, 2.33GHz C2D, 2GB RAM
I think I may have tried that last night, but when I do the program locks up on the second button click.

First, I'd suggest replacing your declaration of textBox with this:

Code:
    IBOutlet NSTextField *textBox;

Also, you're not calling the correct selector on textBox; try setStringValue instead. (The NSTextField declaration above would have allowed the compiler to warn you about using the wrong selector, BTW.)

Code:
    [textBox setStringValue:list];

Third, I expect the error you encountered was because you never retained the NSMutableString object returned by stringWithFormat, as the latter returns an autoreleased object in the classic memory management mode (I'm assuming you haven't enabled the garbage collector on this test program). After your first iteration of "generate" completed and program control returned to the runtime loop, the autoreleased string object was automatically deleted. To fix this, you need to at least add the 2nd line below:

Code:
list = [NSMutableString stringWithFormat:@"%d", [self nextNumber]];

[list retain];

It would be safer to instantiate your NSMutableString in the init method (via alloc and init), rather than in one control path of your generate function. Even though that path ought to be the first one that's executed as the code is currently written, it's unwise to rely upon behavioral side effects like that. Using the init method would not only be safer, but would give you a retained string object that wouldn't give you the error you experienced.

Note that I'm not even touching upon the need to clean up this allocated string object when your program exits. You are getting into a number of areas here, such as Cocoa memory management, for which you need book(s) and a structured approach to learn your way around them; a few posts in a forum aren't going to suffice.
 
OP
J
Joined
Jun 6, 2008
Messages
27
Reaction score
0
Points
1
Your Mac's Specs
Blackbook - 2.4 GHZ Core 2 Duo - 2 GB RAM - 250 GB HDD
Thanks again for your help. You were right on with all of your assumptions and the code works perfectly now.

I am going to the book store right now to read some of Stephen Kochan's first edition objective-c book while I wait for my pre-ordered second edition to come in.

Just one really quick question though, if I retain list manually like that and never release it is the memory released when the program is closed?
 
Joined
Mar 15, 2007
Messages
161
Reaction score
4
Points
18
Your Mac's Specs
17" MacBook Pro, 2.33GHz C2D, 2GB RAM
Just one really quick question though, if I retain list manually like that and never release it is the memory released when the program is closed?

When a program terminates, my understanding is that any allocated memory is normally reclaimed by Mac OS X, whether the memory had been released by the program or not. Memory leaks are primarily an issue while a program is still running. However, it's much smarter to take care of releasing memory and other resources in the program in all cases; failing to do so would be a bit like a Navy fighter pilot who doesn't attempt to catch a wire during a carrier landing, instead trusting to some sort of last ditch*, emergency barricade system to stop his plane from going into the water. :)

Caveat: I'm not an expert in Mac OS X's internal behavior in this regard. All I've ever needed to know for my own purposes as an application developer is that it is always best to make certain you're releasing anything you've allocated once you don't need it anymore.

* is this a pun? I'm not quite sure. :)
 

Shop Amazon


Shop for your Apple, Mac, iPhone and other computer products on Amazon.
We are a participant in the Amazon Services LLC Associates Program, an affiliate program designed to provide a means for us to earn fees by linking to Amazon and affiliated sites.
Top