views:

97

answers:

3

I'm writing an iPhone app. Starting from a view controller in a navigation stack [called EditCreatorController], I am presenting a custom modal view controller [called BMSStringPickerController]. I have created a delegate protocol, etc. per the Apple guidelines for passing data back to the first view and using that view to dismiss the modal view. I even get the expected data back from the modal controller and am able to dismiss it just fine. The problem is, at that point, almost any action I take on the original view controller leads to debugger errors like

-[EditCreatorController performSelector:withObject:withObject:]: message sent to deallocated instance 0x3a647f0

or

-[EditCreatorController tableView:willSelectRowAtIndexPath:]: message sent to deallocated instance 0x3c12c40

In other words, it seems like the original view controller has evaporated while the modal view was showing. This is true no matter which of the two delegate callbacks is invoked.

Here is the code from the parent controller that invokes the modal view:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 1) { // selection on creator type row

    // create a string picker to choose new creator type from list
    BMSStringPickerController *picker = [[BMSStringPickerController alloc] initWithNibName:@"BMSStringPickerController" bundle:nil];
    picker.delegate = self;
    picker.stringChoices = [NSArray arrayWithObjects:@"composer", @"lyricist", @"arranger", @"original artist", @"other", nil];
    picker.currentChoice = creator.type;
    picker.title = @"Creator Type";

    // wrap it in a nav controller so we can get tile bar etc. (from VC prog guide p. 93)
    UINavigationController *newNavigationController = [[UINavigationController alloc]
                                                    initWithRootViewController:picker];

    [self.navigationController presentModalViewController:newNavigationController animated:YES];
    [newNavigationController release];
    [picker release];

 }
}

And here are the delegate callbacks:

 - (void)stringPickerController:(BMSStringPickerController *)picker didPickString:(NSString *)string {
NSLog(@"received string back: %@", string);
typeLabel.text = string; // only change the label for now; object only changes if done button pressed
[self.tableView reloadData];
[self dismissModalViewControllerAnimated:YES];
}

- (void)stringPickerControllerDidCancel:(BMSStringPickerController *)picker {
NSLog(@"picker cancelled");
[self dismissModalViewControllerAnimated:YES];
}

Another weird thing (perhaps a clue) is that although I get the "received string back" NSLog message, and assign it to typeLabel.text (typeLabel is an IBOutlet to a label in my table view), it never appears there, even with the table reload.

Anyone have some ideas?

A: 

Maybe you release the delegate in dealloc of BMSStringPickerController?

Michael Kessler
Bingo! Thanks Michael. So bloody obvious once you pointed it out!This does beg the question: if I don't release it there, do I have a memory leak now? It would seem that my original calling view (the delegate) now has an extra retain on it. Or maybe not -- the declaration in the picker object is: @property (assign) id<BMSStringPickerControllerDelegate> delegate;So since it's assign not retain, I suppose when the picker goes away through the call to dismissModal... everything is clean again memory-wise? (Clearly, I'm still deepening my understanding of obj-c memory management!) :-)
Phil Webster
You right - once the property is not marked with "retain" or "copy" it is not retained and there is no need to release it. You are better to set nil to it...
Michael Kessler
A: 

It may not solve your problem, but I suggest telling the picker to dismiss itself (in the delegate methods), allowing the responder chain to correctly handle the dismiss:

[picker dismissModalViewControllerAnimated:YES];
ohhorob
A: 

The default behavior when there is a memory warning is the release the view of all view controllers that are not visible. So if there was a memory warning while in your modal view controller, its parent view controller could have its view unloaded.

When this happens, viewDidUnload is called on the view controller so that you can release any references you hold into the view. If you have references that you didn't retain they will become invalid when the view is unloaded. Maybe this is happening in your case?

See the UIViewController reference Memory Management section for details. The UIViewController method didReceiveMemoryWarning: releases the view if the view is not currently visible and then calls viewDidUnload.

progrmr