Hillegass Challenge

Discussion in 'Mac Programming' started by BS0D, Feb 23, 2011.

  1. BS0D macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #1
    Hey all,

    I am a newbie at Objective-C. I bought Cocoa Programming for Mac OS X by Hillegass a few days ago and I'm on CHAPTER 5.

    There is this challenge that I'm having a hard time with. I'm supposed to display a string which would contain :

    - the string entered in the text field
    - the number of characters that that strings contains

    in a label (text field).

    Here is my code, maybe you can help me :

    Code:
    #import "AppController.h"
    
    @implementation AppController
    
    -(void)setStringValue:(NSString *)theString {
    	
    }
    
    -(IBAction)countChars:(id)sender {
    
    	NSString *theString = [textField stringValue];
    	NSString *theResult = [theString length];
    	
    	NSLog(@"The RESULT IS : %d", theResult);
    		int length;
    	if ([theString length] == 0) {
    		NSLog(@"la chaine de %@ est vide", textField);
    		[theCount setStringValue:@"Please enter something in the field !"];
    }
                    // works fine up to here
    		NSLog(@"'%@' contains %d characters", theString, theResult);
    		// Here i want to display sthg like "blah blah blah contains 18 characters" ------
                    // but having a hard time with the stringWithFormat: fonction ...
                    // something like 
                    // [NSString stringWithFormat:@"'%@' contains %@ characters...", theString,theResult];
     
    }
    
    @end
    I commented the part where I'm having a hard time using stringWithFormat:

    Can anyone give me a hand, and maybe help me make this code better (i get warnings from Xcode)

    Thanks in advance
     
  2. BS0D thread starter macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #2
    Problem solved:

    Code:
    #import "AppController.h"
    
    
    @implementation AppController
    
    
    -(IBAction)countChars:(id)sender {
    
    	NSString *theString = [textField stringValue];
    	NSString *theResult = [theString length];
    	
    	finalResult = [NSString stringWithFormat:@"'%@' contains %d characters...", theString,theResult];
    		//NSLog(@"The RESULT IS : %d", theResult);
    
    	if ([theString length] == 0) {
    			//NSLog(@"la chaine de %@ est vide", textField);
    		[theCount setStringValue:@"Please enter something in the field !"];
    }
    	else {
    		NSLog(@"'%@' contains %d characters", theString, theResult);
    		[theCount setStringValue:finalResult];
    	}		
    
    }
    
    @end
     
  3. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #3
    The red-hilited part is wrong. The length method returns an int, not an NSString *. The only reason it's not crashing is because you don't send a message to theResult. Just because it works doesn't mean it's correct.

    And you should have received a compiler warning about this mistake. You may think you can ignore it, but you'd be wrong. At lot of things that are flat out errors in Objective-C usage are only presented as compiler warnings.

    Finally, when describing problems, Be Specific. Post actual error messages and warning messages. "I'm having a hard time" is not specific. Posting the exact message and the line it occurs on is specific.
     
  4. BS0D thread starter macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #4
    Hey Chown33,

    Thanks for the heads-up. I was actually expecting someone to correct my code, I'm a total newbie and I'm sure there is a lot of bad things in my code.

    I did not mean to brag, I just said "it works" ... but I know the code could probably be cleaned up real good :)

    I actually get a few warnings from Xcode that I don't know how to fix yet... but at least I passed the challenge ;)

    Is there anything odd in these lines of code ?

    NB: most of the warnings say "Local declaration of 'theString' hide instance variable", what does that mean exactly ?
     
  5. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #5
    Odd in what way? I've already pointed out an error.

    If there's anything else odd, you'll have to explain exactly what you mean.


    What you may mean is "I think something might be odd because I'm getting this warning: [paste warning message here]". If that's what you meant by your question "Is there anything odd..." then you should write that question.

    We can't see your screen. The only code we can see is what you post. Since you only posted one method, we can't see anything else except that one method. A program is always more than one method, so that means there's a whole bunch of your program that we haven't seen. If you think something is odd in it, you have to post it and point out what you think is odd and why.


    As to understanding what the message means: do you know what an instance variable is? What a local variable is? If not, you need to review it.

    What the message means exactly is that you have an instance variable named 'theString' (whose code you haven't posted), and that instance variable is hidden (inaccessible) because you have a local variable also named 'theString' (whose code you did post). There is a way to access the instance variable, but it's usually better if you use different names for instance variables and local variables. A local variable is any variable declared inside the body of a function or method**.


    ** Which isn't preceded by the keyword 'static'.
     
  6. BS0D thread starter macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #6
    Alright, let me clearer then: there IS no other code, this is it.
    I was working on a challenge in chapter 5 of Cocoa Programming for Mac OS X by Hillegass. "Challenge", if you can call it that, but at my level it probably is.

    Again, I am a total newbie. I have some basics in C and that's it, I'm pretty much learning Objective-C on my own, with my book and the doc from Apple.
    The only previous coding experiences I had were in ASM, some C and mostly Applescript Studio -- not very useful, except to understand the bindings and stuff in IB.

    Anyway, so this "challenge" only consists in displaying the number of characters of a string entered by the user in the text field when the button is clicked, that's it. Sound stupid to most of you experienced programmers, but this is a step i have to go through to begin programming more interesting apps -- this is why I don't let go until I've completed the "challenges" in the book :)

    I do indeed need to learn the technical vocab more in depth... methods, classes and instances are the 3 terms that pose the most problems for me right now, I'm having quite a hard time understanding those concepts fully.

    By the way, I fixed it : no more warnings. For some reason I had put theString and another variable in the .h file like this :
    Code:
    #import <Cocoa/Cocoa.h>
    
    
    @interface AppController : NSObject {
    	IBOutlet NSTextField *textField;
    	IBOutlet NSTextField *theCount;
    		//NSString *theResult;
    		//NSString *theString;
    	NSString *finalResult;
    }
    
    -(IBAction)countChars:(id)sender;
    
    @end
    Getting rid of them suppressed the warnings.

    Question though: why do I have to declare finalResult here and not the other NSStrings? This confuses me...
     
  7. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #7
    Your header file contains code. It contains code that defines classes, instance variables, and the declarations of methods or functions. This is code that declares the data which methods (actions) act upon.

    Code consists of both data and actions. Data consists of classes, instance variables, global variables, and the declared types and names of functions and methods. Actions consist of method and function bodies. It's not possible to fully understand what an action does without seeing all the data it acts on. That means all the relevant classes, variables, and method declarations.

    The reason you have to declare finalResult in the header is because you didn't declare it in the method. If you declare it in the method, you can remove it from the @interface.

    Since there are no other methods, then finalResult can safely be a local variable. You do, however, have to declare it as a variable in the method where it's used.


    If you had two or more methods that both needed to use a single consistent finalResult, then you would have to declare it as an instance variable. Instance variables have a lifetime outside of individual methods or functions.

    Local variables only exist as long as their enclosing method or function is active. When the function or method returns, local variables disappear.

    This is mostly just plain ordinary C, which has local variables, static variables, malloc'ed memory, and functions. Instance variables and methods are just special cases of those.
     
  8. BS0D, Feb 23, 2011
    Last edited by a moderator: Feb 23, 2011

    BS0D thread starter macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #8
    Thanks, this is clearer now.
    But, another question: why does finalResult need to be declared in the .h file? Is it a Local variable as it is?
    If I wanted to make it a global variable, how would I need to declare it (in what form I mean)?

    See, I get the gist now that you explain but I will still have to dig in and look at more examples. The problem with books and e-learning resources out there on Obj-C is that they don't spend enough time clearing up those concepts before moving on to the coding part.
    Next thing you konw, they comment the lines of code they've just made you write and harrass you with terms such as "instances", "instance variable", "selectors", global variable", "class", "subclass", "methods", "sender" and many others.
    Sure enough you've heard and read those words EVERYWHERE thousands of times, but you still struggle figuring out what the **** they are !

    Anyway, thanks for your time ;)
     
  9. monsieurpaul, Feb 23, 2011
    Last edited: Feb 23, 2011

    monsieurpaul macrumors regular

    Joined:
    Oct 8, 2009
    #9

    finalResult doesn't "need" to be declared in the .h file, you can, and maybe should, declare it inside your countChar method if it is not used elsewhere.

    You could also declares all your string variables as instances variables in the header. But you'll have to modify your method to not "recreate" them as local variable.

    The Hillegass book is really great, but you may be rapidly overwhelmed by all the power of Cocoa. There are others great books that focus more on the Objective-C logic such as "Programming in Objective-C 2.0" by Stephen G. Kochan or "Learn Objective-C on the Mac" by Mark Dalrymple and Scott Knaster.
     
  10. BS0D thread starter macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #10
    I have these books too, sir Paul! :D
    Thanks for the advice, but I don't know how to rewrite all that without having to declare them variables as globals.

    I chose to take it one book at a time... reading them all at the same time confuses me more than anything!

    I have another issue here... I get the warning message "class AppController does not implement NSspeechSynthesizer".
    I found something online that said you had to add <NSSpeechSynthesizerDelegate> next to the class definition.
    Where is that?

    If I do this in my .h file :

    Code:
    #import <Cocoa/Cocoa.h>
    
    @interface AppController : NSObject [COLOR="Red"]<NSSpeechSynthesizerDelegate>[/COLOR] {
    
    	IBOutlet NSTextField *textField;
    	NSSpeechSynthesizer *speechSynth;
    	IBOutlet NSButton *stopButton;
    	IBOutlet NSButton *startButton;
    }
    
    -(IBAction)sayIt:(id)sender;
    -(IBAction)stopIt:(id)sender;
    
    @end
    ... then it compiles fine and without a warning, but the stop button stays enabled after the reading. So I have to click on "stop" first, and then "start reading" again :/
     
  11. wlh99 macrumors 6502

    Joined:
    Feb 7, 2008
    #11
    Is the console open? (Run->Console) There is probably a runtime error you can't see. That is why it stops but the stop button is still active. Open the Console and then run the program.
    If you have Kochan's book also, that's the better book to start with. It will answer your questions about the declarations etc., before you get into more complicated situations.
     
  12. monsieurpaul macrumors regular

    Joined:
    Oct 8, 2009
    #12
    Me too, that's why I started with the Kochan's book ;)
     
  13. BS0D thread starter macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #13
    Well I have set the console to open automatically when I build the app. There is nothing there but the NSLog statements I set up in my code...
    No error, no warning. Nothing.

    If I remove "<NSSpeechSynthesizerDelegate>" from the class declaration however, I get this :
    Code:
    [speechSynth setDelegate:self];  
    // warning on this line: [B]class 'AppController' does not implement the 'NSSpeechSynthesizerDelegate' protocol[/B]
    Here is the full code from the init method by the way :

    Code:
    -(id)init {
    	[super init];
    	NSLog (@"init");
    		// nouvelle instance de speechSynthetizer avec voix par défaut.
    	speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
    	voiceList = [[NSSpeechSynthesizer availableVoices] retain];
    	[B][speechSynth setDelegate:self]; // this line apparently has no effect, I think that is the reason why the stop button is not greyed out after it's done reading...[/B]
    	return self;
    }
    I have no idea what is wrong... I don't know how to fix this.
    When I debug the app, it seems like it never gets to that part of the code...
     
  14. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #14
    Never gets to what part of the code?


    You haven't posted any implementation code for these methods:
    Code:
    -(IBAction)sayIt:(id)sender;
    -(IBAction)stopIt:(id)sender;
    
    You also haven't posted any implementation code for the methods of NSSpeechSynthesizerDelegate. Declaring that your class implements the protocol doesn't give you any implementation. You have to provide those.
     
  15. BS0D thread starter macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #15
    Code:
    #import <Cocoa/Cocoa.h>
    
    
    @interface AppController : NSObject <NSSpeechSynthesizerDelegate> {
    
    	IBOutlet NSTextField *textField;
    	NSSpeechSynthesizer *speechSynth;
    	
    	IBOutlet NSButton *stopButton;
    	IBOutlet NSButton *startButton;
    	IBOutlet NSTextField *info;
    	
    	IBOutlet NSTableView *tableView;
    	NSArray *voiceList;
    }
    
    -(IBAction)sayIt:(id)sender;
    -(IBAction)stopIt:(id)sender;
    
    @end
    Code:
    #import "AppController.h"
    
    
    @implementation AppController 
    
    -(id)init {
    	[super init];
    	NSLog (@"init");
    		// nouvelle instance de speechSynthetizer avec voix par défaut.
    	speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
    	voiceList = [[NSSpeechSynthesizer availableVoices] retain];
    	[speechSynth setDelegate:self];
    	return self;
    }
    
    -(void)awakeFromNib {
    	NSString *defaultVoice = [NSSpeechSynthesizer defaultVoice];
    	int defaultRow = [voiceList indexOfObject:defaultVoice];
    	[tableView selectRowIndexes: [NSIndexSet indexSetWithIndex:defaultRow] byExtendingSelection:NO];
    	[tableView scrollRowToVisible:defaultRow];
    }
    
    -(void)speechSynthetizer:(NSSpeechSynthesizer *)sender 
    	   didFinishSpeaking:(BOOL)complete
    {
    	NSLog(@"End of = %d", complete);
    	[stopButton setEnabled:NO];
    	[startButton setEnabled:YES];
    	[tableView setEnabled:YES];
    }
    
    - (IBAction)sayIt:(id)sender {
    	
    	NSString *string = [textField stringValue];
    		//la chaine est elle vide?
    	if ([string length] == 0) {
    		NSLog(@"la chaine de %@ est vide", textField);
    		[info setStringValue:@"Please enter the text to read first !"];
    		return;
    	}
    	
    	[speechSynth startSpeakingString:string];
    	[stopButton setEnabled:YES];
    	[startButton setEnabled:NO];
    	[tableView setEnabled:NO];
    	[info setStringValue:@"Type the text to read, then select a voice in the list and click 'Start Reading'."];
    }
    
    - (IBAction)stopIt:(id)sender {
    	[speechSynth stopSpeaking];
    	[stopButton setEnabled:NO];
    	[startButton setEnabled:YES];
    	[tableView setEnabled:YES];
    }
    
    -(int)numberOfRowsInTableView:(NSTableView *)tv {
    	return [voiceList count];
    }
    
    - (id)tableView:(NSTableView *)tv
    	objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
    {
    	NSString *v = [voiceList objectAtIndex:row];
    	NSDictionary *dict = [NSSpeechSynthesizer attributesForVoice:v];
    	return [dict objectForKey:NSVoiceName];
    }
    
    -(void)tableViewSelectionDidChange:(NSNotification *)notification {
    	int row = [tableView selectedRow];
    	if (row == -1) {
    		return;
    	}
    	NSString *selectedVoice = [voiceList objectAtIndex:row];
    	[speechSynth setVoice:selectedVoice];
    	NSLog (@"New Voice = %@", selectedVoice);
    }
    
    @end
    
     
  16. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #16
    Spelling error hilited in red.

    This doesn't cause a compiler error or even a warning, because all the protocol's methods are optional. That puts an extra burden on you to make sure method names are spelled correctly.

    I recommend opening the header (NSSpeechSynthesizer.h) and copying and pasting. Or use code completion in Xcode.
     
  17. GorillaPaws macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #17
    That's the right way to do it.

    OP: Start with Kochan and then do Hillegass. Kochan is less "sexy" because you're not creating pretty GUIs, but it's essential to understand before moving forward. You're struggling with the concepts surrounding variable scope, which can be a bit tricky to understand when it's best to make an Ivar or when to use locals, and how variables work in Objective-C.
     
  18. BS0D thread starter macrumors newbie

    BS0D

    Joined:
    Aug 16, 2008
    #18
    OMG, can't believe I didn't see that ! Thanks a lot Chown33, everything works great now.

    Will definitely do that now :)
     

Share This Page