Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
When my OS X app closes (via the Quit button in the menu), I need to write out a text file. The applicationWillTerminate method isn't being called. It's in my app delegate. I've spent hours & hours reading posts on the same problem, with no resolution. The Apple doc didn't help either.
 

chown33

Moderator
Staff member
Aug 9, 2009
10,679
8,303
A sea of green
Post your code.

A common cause of uncalled delegate methods is incorrect spelling (usually a typo) or wrong parameters. Without seeing your actual code, that would be my first guess.
 

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
Post your code.

A common cause of uncalled delegate methods is incorrect spelling (usually a typo) or wrong parameters. Without seeing your actual code, that would be my first guess.

Code:
- (void)applicationWillTerminate:(NSNotification *)notification{
		[self putSettings:(nil)];
}
 

chown33

Moderator
Staff member
Aug 9, 2009
10,679
8,303
A sea of green
Please explain how you know the method isn't being called.

Did you set a breakpoint on it and see if it's being triggered? Is there some code like an NSLog in putSettings:? Exactly what code is in putSettings: and what does nil as a parameter mean?

What other methods of NSApplicationDelegate are implemented? Is one of them applicationShouldTerminate:? Have you set a breakpoint on that? How about any of the application-launch methods?

What have you tried to debug this?

http://www.mikeash.com/getting_answers.html
 
Last edited:

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
Please explain how you know the method isn't being called.

Did you set a breakpoint on it and see if it's being triggered? Is there some code like an NSLog in putSettings:? Exactly what code is in putSettings: and what does nil as a parameter mean?

What other methods of NSApplicationDelegate are implemented? Is one of them applicationShouldTerminate:? Have you set a breakpoint on that? How about any of the application-launch methods?

What have you tried to debug this?

http://www.mikeash.com/getting_answers.html

I set breakpoints and put NSLogs in both methods. I have tried putSettings defined as an action, and as returning void. nil was a kludge to satisfy the compiler.

I did have applicationShouldTerminate: with breakpoint an NSLog, but it wasn't getting called either.

- (void)awakeFromNib{ works.


Code:
//- (IBAction)putSettings:(id)sender;{
- (void)putSettings;{
	NSString *line1 = portsButton.titleOfSelectedItem;
	NSError *err = nil;
	NSString *path = [[NSBundle mainBundle] pathForResource:@"RotorSettings" ofType:@"txt"];
	
	
	line1 = [line1 stringByAppendingString:(portsButton.titleOfSelectedItem)];
	line1 =[line1 stringByAppendingString:(@":")];
	line1 =[line1 stringByAppendingString:(baudButton.titleOfSelectedItem)];
	
	if(![line1 writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]) {
    //handle error

	}
}
 

chown33

Moderator
Staff member
Aug 9, 2009
10,679
8,303
A sea of green
I set breakpoints and put NSLogs in both methods.

Show your code with the NSLogs. If you got no output, then say that you got no output.

We can't read your mind. We don't know what you did unless you tell us. We can't read your screen or see your code unless you post it. I gave the link of the Getting Answers article for a reason: you're not providing adequate information or explanation for debugging.

Debugging is detective work. You have to gather evidence, get information, and put logical connections together. You have to build the case and present your findings so others can see the evidence, information, and logical connections for themselves. Without evidence, all you have is unsubstantiated beliefs and expectations.


I have tried putSettings defined as an action, and as returning void. nil was a kludge to satisfy the compiler.
What was the error message or warning that made you think you need a kludge? Maybe what you did to satisfy the compiler is the cause of things not working.

An error message or warning is evidence. The code that causes the message is also evidence. Naively applying kludges in order to make the visible problem go away isn't necessarily a real solution.


I did have applicationShouldTerminate: with breakpoint an NSLog, but it wasn't getting called either.
So you have two methods of the same delegate object that aren't being called. This is evidence.

Logically speaking, what might be some possible causes for multiple methods of the same delegate object not being called?

I want you to think of some possible answers for this question. I already outlined one in my first reply: misspelled method names. So think of some others before continuing.



The first thing that comes to mind for me is that the object you think is acting as app-delegate isn't really the app-delegate. Either some other object is the real app-delegate, or no object is.

So think about how you'd gather evidence and information that the object you think is app-delegate is really the app-delegate. Start by describing how you set the app-delegate. Then show the code for the entire app-delegate class, not just isolated methods of it.


- (void)awakeFromNib{ works.
The NSApplicationDelegate protocol doesn't have an awakeFromNib method. So if that method is being called, it's not because the object receiving the message is the app-delegate. It's because that object is playing another role, one associated with loading a nib.


Code:
//- (IBAction)putSettings:(id)sender[COLOR="Red"];[/COLOR]{
- (void)putSettings[COLOR="red"];[/COLOR]{
	NSString *line1 = portsButton.titleOfSelectedItem;
	NSError *err = nil;
	NSString *path = [[NSBundle mainBundle] pathForResource:@"RotorSettings" ofType:@"txt"];
	
	
	line1 = [line1 stringByAppendingString:(portsButton.titleOfSelectedItem)];
	line1 =[line1 stringByAppendingString:(@":")];
	line1 =[line1 stringByAppendingString:(baudButton.titleOfSelectedItem)];
	
	if(![line1 writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]) {
    //handle error

	}
}
This code has a serious problem, a misplaced semicolon, which I've hilited in red.

I don't know what this will do as written, but I can tell what it doesn't do. It doesn't define the brace-enclosed block as the body of a method. To define a method, the opening brace must be the first non-whitespace non-comment after the last parameter name - no semicolon.

If you copy-pasted the method declaration from your @interface (I'm guessing, since you haven't posted code), then you must replace the semicolon with a { } block, not append a { } block after the semicolon.

FWIW, all C-like languages work this way. The body of the function or method goes directly after the declaration of the name and parameters. Depending on exactly which language, an intervening semicolon may result in invalid code, which the compiler will reject, or it may result in syntactically correct code, which the compiler will accept, but with a completely different and unintended meaning.
 

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
Show your code with the NSLogs.
...

Good eye on the semi-colon. I'll post this code again, with NSLog added. It really doesn't change anything.

One thing that hit me was that code completion didn't kick in when I added the applicationWillTerminate method. Should it have? The Apple docs say it's defined in application.h. That's not a part of my project, and I can't find it on my box.

I don't remember the error message. And since I re-declared the method, it's no longer relevant. Here's the whole thing.
Code:
//
//  AppController.m
//  AMSerialTest
//
//		2009-09-09		Andreas Mayer
//		- fixed memory leak in -serialPortReadData:


#import "AppController.h"
#import "AMSerialPortList.h"
#import "AMSerialPortAdditions.h"

@implementation AppController
@synthesize serialPort;

- (void)awakeFromNib
{
	
	// register for port add/remove notification
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddPorts:) name:AMSerialPortListDidAddPortsNotification object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRemovePorts:) name:AMSerialPortListDidRemovePortsNotification object:nil];
	[AMSerialPortList sharedPortList]; // initialize port list to arm notifications
    
	[buttonClose setEnabled:NO];
	[buttonConnect setEnabled:NO];
	[self enableCommUI:NO];
  
  [[buttonLED cell] setBackgroundColor:[NSColor redColor]];
	
	[baudButton removeAllItems];
	[baudButton addItemWithTitle:(@"Baud")];
	[baudButton addItemWithTitle:(@"600")];
	[baudButton addItemWithTitle:(@"1200")];
	[baudButton addItemWithTitle:(@"2400")];
	[baudButton addItemWithTitle:(@"4800")];
	[baudButton addItemWithTitle:(@"9600")];
	[baudButton addItemWithTitle:(@"19200")];
	[baudButton addItemWithTitle:(@"38400")];
	[baudButton addItemWithTitle:(@"76800")];
    
	[portsButton removeAllItems];
	[portsButton addItemWithTitle:(@"Ports")];
	NSString *pathString;
	NSString *compString = @"usbserial";

	//exclude except serial adapters
	for (AMSerialPort* aPort in [[AMSerialPortList sharedPortList] serialPorts]) {
		pathString = [aPort bsdPath];
		if ([pathString rangeOfString:compString].location != NSNotFound) {
			[portsButton addItemWithTitle:(pathString)];
		}
	}
	[self getSettings:(nil)];
}


- (void)applicationWillTerminate:(NSNotification *)notification{
		NSLog(@"Drat!");

		[self putSettings];
}

- (void)dealloc
{
	[[NSNotificationCenter defaultCenter]removeObserver:self];
	[self cleanUp];
	[super dealloc];
}

-(void)enableCommUI:(BOOL)flag{
	[inputTextField setEnabled:flag];
	[buttonSend setEnabled:flag];
}

-(void)showAlert:(NSString *)msg{
	NSAlert *alert = [[NSAlert alloc] init];
	[alert setMessageText:msg];
	[alert beginSheetModalForWindow:[NSApplication sharedApplication].mainWindow
                      modalDelegate:nil didEndSelector:nil contextInfo:nil];
	[alert release];
}

-(void)openConnection{
	[self initPort];
}

-(IBAction)buttonConnect_Click:(id)sender{
	if(portsButton.indexOfSelectedItem == 0){
		[self showAlert:@"Please select a port to connect to."];
	}
	else if(baudButton.indexOfSelectedItem == 0){
		[self showAlert:@"Please select baud rate."];
	}
	else{
		[self openConnection];
	}
}

-(IBAction)buttonClose_Click:(id)sender{
  
	[self cleanUp];
	[self enableCommUI:NO];
	[buttonConnect setEnabled:NO];
	[buttonClose setEnabled:NO];
}

- (void)initPort{
	NSString *deviceName = portsButton.titleOfSelectedItem;
	NSString *errMsg = @"Couldn't open port: ";
	errMsg = [errMsg stringByAppendingString:deviceName];
	if (![deviceName isEqualToString:[self.serialPort bsdPath]]) {
		[self.serialPort close];
        
		self.serialPort = [[[AMSerialPort alloc] init:deviceName 
                                             withName:deviceName 
                                                 type:(NSString*)CFSTR(kIOSerialBSDModemType)] autorelease]        
        
		// register as self as delegate for port
		[self.serialPort setDelegate:self];
		
		// open port - may take a few seconds ...
		if ([self.serialPort open]) {
			
			[self enableCommUI:YES];
			[buttonConnect setEnabled:NO];
			[buttonClose setEnabled:YES];
                  
			unsigned long dbits = (8);
			long baudRate = [baudButton.titleOfSelectedItem longLongValue];
			[self.serialPort setSpeed:(baudRate)];
			[self.serialPort setParity:(kAMSerialParityNone)];
			[self.serialPort setStopBits:(kAMSerialStopBitsOne)];
			[self.serialPort setDataBits:(dbits)];
			[self.serialPort commitChanges];
            
			[[buttonLED cell] setBackgroundColor:[NSColor greenColor]];
      
	
			// listen for data in a separate thread
			[self.serialPort readDataInBackground];
			
		} else { // an error occured while creating port
            
			[self enableCommUI:NO];
			[buttonConnect setEnabled:NO];
			[buttonClose setEnabled:NO];
            
			[[buttonLED cell] setBackgroundColor:[NSColor redColor]];
      [self showAlert:errMsg];
			self.serialPort = nil;
		}
	}
}

-(void)cleanUp{
	if(self.serialPort){
		[self.serialPort setDelegate:nil];
		[self.serialPort free];
		self.serialPort = nil;
	}
}

- (void)serialPortReadData:(NSDictionary *)dataDictionary
{
	// this method is called if data arrives 
	// @"data" is the actual data, @"serialPort" is the sending port
	AMSerialPort *sendPort = [dataDictionary objectForKey:@"serialPort"];
	NSData *data = [dataDictionary objectForKey:@"data"];
	if ([data length] > 0) {
		NSString *text = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
		//[outputTextView insertText:text];
		[text release];
		// continue listening
		[sendPort readDataInBackground];
	} else { // port closed
		//[outputTextView insertText:@"port closed\r"];
	}
}

- (IBAction)chooseDevice:(id)sender
{
// new device selected
	[self initPort];
}

- (IBAction)send:(id)sender
{
	NSString *sendString = @"AP1";
	sendString = [sendString stringByAppendingString:([inputTextField stringValue])];
	sendString = [sendString stringByAppendingString:@";AM1;\r\n"];
//    if (segmentTrail.selectedSegment == 0) {
  
	if(!self.serialPort) {
		[self buttonConnect_Click:nil];
	}

	if([self.serialPort isOpen]) {
		[self.serialPort writeString:sendString usingEncoding:NSUTF8StringEncoding error:NULL];
	}
}

- (IBAction)checkSelected:(id)sender {
	if (portsButton.indexOfSelectedItem > 1 && baudButton.indexOfSelectedItem > 1){
		[buttonConnect setEnabled:YES];
	}
}

- (IBAction)getSettings:(id)sender {
	NSError *err = nil;
	NSString *path = [[NSBundle mainBundle] pathForResource:@"RotorSettings" ofType:@"txt"];
	NSString *contents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err];
	if(!contents) {
    //handle error
	}
}

//- (IBAction)putSettings:(id)sender;{
- (void)putSettings {
	NSString *line1 = portsButton.titleOfSelectedItem;
	NSError *err = nil;
	NSString *path = [[NSBundle mainBundle] pathForResource:@"RotorSettings" ofType:@"txt"];
	
	
	line1 = [line1 stringByAppendingString:(portsButton.titleOfSelectedItem)];
	line1 =[line1 stringByAppendingString:(@":")];
	line1 =[line1 stringByAppendingString:(baudButton.titleOfSelectedItem)];
	
	if(![line1 writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&err]) {
    //handle error
	}
}
@end

Cocoa is new to me and I'm just trying to get some understanding of it. Delegates are pretty much a mystery right now. You may be onto something that I'm not working with a delegate. I'm very unsure what's relevant and what's not. That's my story and I'm sticking to it....:D

What I started with is AMSerialPort from SourceForge.
 
Last edited by a moderator:

gnasher729

Suspended
Nov 25, 2005
17,980
5,565
Just wondering... If you check the method menu in the editor, does it list applicationWillTerminate: as a method of your app delegate class? Is it in the same class as applicationWillFinishLaunching: ?
 

chown33

Moderator
Staff member
Aug 9, 2009
10,679
8,303
A sea of green
I may have asked this on another thread of yours, but please identify your OS and Xcode versions.

Also tell us how many other Mac apps you've written, either from scratch or by taking an existing app and modifying it.

What tutorials or books have you completed? Did you do all the exercises listed in the books?


The AMSerialPort download contains an Xcode project that compiles under OS X 10.6.8 and Xcode 3.2.1. That's the Snow Leopard configuration I have, and I just built and ran it. I have no additional serial-ports on this config right now, so it only showed the builtin /dev/cu.* devices. It launched and presented a list as expected, but I went no further.

The source is older than 10.6, because it doesn't use the NSApplicationDelegate protocol, which was first present in 10.6:
http://developer.apple.com/library/...ionDelegate_Protocol/Reference/Reference.html

The connection between NSApplication and the AMSerialTest project's AppController object is made in the nib file. Or at least that's how it appears in my cursory look at the project.

Unfortunately, the AppController class provided doesn't implement any of the NSApplicationDelegate protocol's methods, so that connection may or may not really work. If it does, then something you did broke it, and you should go back to the original project (unzip it again), and try building it exactly as provided. Then add exactly one method containing an NSLog() statement, preferably one of the app-launch methods, and see if that method is called. This is the Simplest Thing That Could Possibly Work, a useful debugging principle to keep in mind.

If the single app-launch delegate method isn't called, as evidenced by the absence of log output, then my guess is that the original AMSerialTest project doesn't have the app-delegate properly connected. However, if it is called, as evidenced by log output, then I'd guess you broke something somewhere during the course of your modifications.


When I add this method to my copy of an otherwise virgin AppController.m:
Code:
-(void)applicationDidFinishLaunching:(NSNotification *) notice
{
   NSLog( @"Hello from did finish launching" );
}
and change nothing else, I see the expected output in my log. Therefore, I conclude that the AMSerialPort project, as obtained from the following website, does indeed have the app-delegate connection set correctly. Tested on OS 10.6.8, Xcode 3.2.1.

http://www.harmless.de/cocoa-code.php


Based on the above, I suspect you broke something in the nib, or somehow modified it so an instance of type AppController isn't being set as the app-delegate.

I don't know a simple way to fix this, so I suggest learning how app-delegates work using a simpler project, perhaps one of Apple's sample code projects linked from the NSApplicationDelegate class reference.

It's also conceivable that the AMSerialTest project doesn't work on your OS and Xcode version. Not knowing what those are, I won't speculate further. However, since I outlined a simple modification that does work, and does demonstrate that an app-delegate is set, you could try that with a virgin copy of your AMSerialTest and see what happens.

If the simple change to AMSerialTest works, then you might try copy-pasting your code into it, in small pieces. After every change, you should rebuild, retest, and confirm everything works correctly. That's another useful debugging principle: build and test often.


Based on everything you've posted so far, I think you're in way over your head, and way beyond your proficiency level. You're trying to create something without understanding the foundations of what it's built on. For example, the building-blocks of delegation (app-delegate in particular), or even the basic syntax (the semicolon bug and the fact you felt it necessary to kludge around it rather than ask someone what the problem might be). You need to start simpler, make those simpler things work reliably, understand everything they do, and then try the more complex program.

When I was working as an instructor many years ago, I told students to write at least 4 separate programs that were simpler than the one they wanted to write first. I also recommended that they choose one facet of the program they wanted to write, and incorporate just that one facet into a separate program. Once they got it working, they could go on to write another program incorporating a different facet. This got them to see that things are often much more complex than first envisioned, and also got them to see the value of making simplified test programs.

Another possible path is to switch to a more forgiving language and dev platform, such as RealBasic. If your goal is mainly to write a program for your antenna rotor, it may start a lot closer to that goal than Xcode is (or even than Xcode + AMSerialPort).

EDIT

I just noticed your putSettings method is writing to the app-bundle. Do not do this. Do not attempt to do this. There will come a time when it will fail, and you won't know why.

Use the NSUserDefaults class for storing simple settings and non-volatile data.
 
Last edited:

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
Just wondering... If you check the method menu in the editor, does it list applicationWillTerminate: as a method of your app delegate class? Is it in the same class as applicationWillFinishLaunching: ?

I'm not sure what you are calling the method menu. Are you referring to the second icon from the left in the Navigator panel (the leftmost panel)? If so, applicationWillTerminate is listed, but AFAICT, I don't have an app delegate. I reconnected to File's Owner and it's working.
 
Last edited:

Dreamspinner

macrumors member
Original poster
Dec 17, 2012
39
0
I may have asked this on another thread of yours, but please identify your OS and Xcode versions.

Also tell us how many other Mac apps you've written, either from scratch or by taking an existing app and modifying it.

What tutorials or books have you completed? Did you do all the exercises listed in the books?

.......

EDIT

I just noticed your putSettings method is writing to the app-bundle. Do not do this. Do not attempt to do this. There will come a time when it will fail, and you won't know why.

Use the NSUserDefaults class for storing simple settings and non-volatile data.

My OS version is 10.8.2 and Xcode version is 4.5.2. This is my first attempt at a Mac program.

I took a course at Big Nerd Ranch and completed all the tutorials. It really wasn't for absolute newbies. Professionally, I have coded in COBOL, CICS and Delphi. Personally, I've had success with BASIC, QBasic, C, C++, Turbo Pascal and Delphi. Cocoa is for some reason the most arcane I've tried. Maybe age. I WILL master this.

I've read the other BNR books, Mastering Xcode 4 by Nozzi, cocoa and Objective C by O'Reilly and a Dummies (obsolete) book.

Yahoozers! It's working. I took your suggestion to get a fresh copy of AMSerial, and both methods worked. Then I took a look at the .nib in the editor and in the panel (one book I have calls it the dock) between the left one and the editor panel, I right clicked on the AppController object and saw it had a Referencing Outlet (delegate) to File Owner. That wasn't there in my project, and I added it.

Thanks for your tutelage and persistence. Next is to tackle your advice on writing (and reading) my settings
 
Last edited by a moderator:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.