Tim
Tim

Reputation: 4101

ViewController load methods called when view disappears?

I've got the following code in a viewController. I've got a NavigationController on the view (which is the child view - the code for the parent is working fine)

What happens is when I select an option on the parent, this viewController loads. The user can select an option from the child viewController to open a PDF file with a DocumentInteractionController (which works fine).

The problem is when I try going back to the parent viewController, messages are being sent to the child viewController as if it's still allocated. I saw something similar when I set it up since there were multiple calls to the methods in the child viewController.

Any thoughts on what I'm doing wrong?

#import "DetailViewController.h"

@interface DetailViewController ()

@end

@implementation DetailViewController

@synthesize node;
@synthesize replies;
@synthesize docController;

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.tableView reloadData];
    [self.tableView setContentOffset:CGPointZero animated:NO];
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.docController init];
    // Do any additional setup after loading the view from its nib.
}

- (void) dealloc
{
    [self.docController release];
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (self.replies == nil)
    {
        self.replies = [[NSArray alloc] init];
        self.actions = [[NSArray alloc] init];
    }
    if(self.replies.count == 0)
    {
        self.replies = [self.node nodesForXPath:@"./question/reply/text" error:nil];
        self.actions = [self.node nodesForXPath:@"./question/reply/response/action" error:nil];
    }

    return self.replies.count;
}

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"QuestionCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    // Get the object to display and set the value in the cell
    NSString *cellText = [[replies objectAtIndex:indexPath.row] stringValue];
    cell.textLabel.text = cellText;
    return cell;
}

- (void) showOptionsMenu:(NSString *) fileName
{

    NSString *fileToOpen = [[NSBundle mainBundle] pathForResource:fileName ofType:@"pdf"];
    NSURL *fileURL = [NSURL fileURLWithPath:fileToOpen];

    self.docController = [self setupControllerWithURL:fileURL usingDelegate:self];

    bool didShow = [self.docController presentOptionsMenuFromRect:CGRectMake(0, 0, 150, 150) inView: self.view animated:YES];

    if(!didShow)
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"Sorry, app not found" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
    }

}

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *action = [[self.actions objectAtIndex:indexPath.row] stringValue];
    [self showOptionsMenu:action];
}

- (UIDocumentInteractionController *) setupControllerWithURL: (NSURL *) fileURL usingDelegate:(id <UIDocumentInteractionControllerDelegate>) interactionDelegate
{
    UIDocumentInteractionController *interactionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
    interactionController.delegate = interactionDelegate;
    return interactionController;
}


@end

EDIT

Adding the code for the parent view controller...maybe there's something I'm doing wrong in there? I'm using GDataXML to load a Q&A app based on the contents of an XML file...

@implementation ViewController

@synthesize currentReply;
@synthesize questions;

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setUpQuestions];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)dealloc
{
    [super dealloc];
}

- (void) setUpQuestions
{
    // create and init NSXMLParser object

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"query" ofType:@"xml"];
    NSData *xml_data = [[NSData alloc] initWithContentsOfFile:filePath];
    NSError *error;
    GDataXMLDocument *xmlDoc = [[GDataXMLDocument alloc] initWithData:xml_data options:0 error:&error];

    NSArray *rootDataArray = [xmlDoc.rootElement nodesForXPath:@"//query" error:nil];
    for (GDataXMLElement *rootDataElement in rootDataArray)
    {
        // Allocate the query object
        self->query = [[[Query alloc] init] autorelease];

        // Name
        NSArray *query_title = [rootDataElement elementsForName:@"text"];
        if (query_title.count > 0)
        {
            GDataXMLElement *queryTitle = (GDataXMLElement *) [query_title objectAtIndex:0];

            self->query.queryTitle = [[[NSString alloc] initWithString:queryTitle.stringValue] autorelease];
        }


        NSArray *query_first_question = [rootDataElement elementsForName:@"question"];
        NSArray *replies = [NSArray alloc];
        questions = [[NSMutableArray alloc] init];
        if(query_first_question.count == 1)
        {
            GDataXMLElement *fq = (GDataXMLElement *) [query_first_question objectAtIndex:0];
            replies = [fq elementsForName:@"reply"];
            for (GDataXMLElement *replyElement in replies)
            {
                [questions addObject:replyElement];
            }
        }
    }
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Only one section.
    return 1;
}

- (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection:(NSInteger)section
{
    switch(section)
    {
        case 0:
            return questions.count;
            break;
        case 1:
            return 1;
            break;
    }

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"QuestionCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
    }

    // Get the object to display and set the value in the cell.
    GDataXMLElement *questionAtIndex = questions[indexPath.row];
    NSString *cellText = [[[questionAtIndex elementsForName:@"text"] objectAtIndex:0] stringValue];
    cell.textLabel.text = cellText;
    //cell.textLabel.text = [[questionAtIndex elementsForName:@"text"] objectAtIndex:0];
    return cell;
}


- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //NSMutableString *msg = [NSMutableString new];
    //[msg appendString:@"You selected row: "];
    //[msg appendString:[NSString stringWithFormat:@"%i",indexPath.row]];

    //UIAlertView *alertMsg = [[UIAlertView alloc] initWithTitle:@"Row Selected" message:msg delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];

    //[alertMsg show];
    if (questions != nil)
    {
        GDataXMLElement *selectedReply = (GDataXMLElement *) [questions objectAtIndex:indexPath.row];
        DetailViewController *dvc = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
        dvc.node = selectedReply;
        [self.navigationController pushViewController:dvc animated:YES];
        [dvc release];
    }
}

EDIT

I've tried profiling and looking for zombies, but when the crash occurs there are no zombie objects flagged. It throws the following error in the console:

[UIView _forgetDependentConstraint:]: message sent to deallocated instance 0x1e8ab810

Upvotes: 0

Views: 1148

Answers (3)

Bhavin
Bhavin

Reputation: 27225

I have seen this Issue before also !!!

Answer : Turn Off "AutoLayout".

I am guessing the error occurred due to new feature in ios called AutoLayout. It looks like Compiler has created some NSLayoutConstraint objects and due to some reason the objects were released more than they should. Deletion and Re-Creation, forces Xcode to Re-Build the Constraints. But,I am not 100% sure.

Try to Un-Check "AutoLayout", if it can solve your Problem.

Upvotes: 1

iDevzilla
iDevzilla

Reputation: 15558

From what I understood, your parent view controller is setting the node here:

    dvc.node = selectedReply;

and it's never being released from your DetailViewController.

I'm assuming that your GDataXMLElement in the DetailViewController header is set as "retain".

And there's some leaking problems as icodestuff pointed out.

Upvotes: 0

icodestuff
icodestuff

Reputation: 350

Your DetailViewController code is fine - not actually fine, as you're leaking self.replies and self.actions, and the [self.docController init] is very odd and probably wrong (always alloc and init together) - but the lifecycle code on this end looks fine. The problem is almost certainly in the parent view controller (or possibly the document controller if you're creating a retain cycle there). If the parent view controller is holding onto a pointer to the detail view controller, it won't actually be deallocated and accessing the view or any property thereof will cause -viewDidLoad to be called again.

Upvotes: 0

Related Questions