views:

209

answers:

1

Greetings,

I have a strange issue when calling .navigationController popViewControllerAnimated:YES from an NSOperation where the screen does not properly update and leaves the popped view visible but apparently deallocated.

Details: I have a list view that accesses a local sqlite database. When tapping on a record, it displays that record's detail view and kicks off an asynchronous NSOperation (on the main thread) to check the XML back end database to see if that record is still available in the database. For architectural reasons I won't get into, I can't run this operation on the list view, it must be in the detail view. If the record was deleted since the last synchronization, it notifies the user that the record has been deleted and calls the popViewControllerAnimated on a shared app delegate. Ideally, this would pop the current detail view and return to the previous list view.

Error: The proper view is in fact popped in memory (it has been deallocated) but it is still displayed on the screen. The navigation bar is blank, but I can click on the place where the "back" button should be, and it navigates to the start screen prior to the list view, meaning that at least the navigation bar understands where it's supposed to be, even if it's not displaying the title or back buttons properly. The primary issue is that the detail view is still visible and can be interacted with, which causes the application to crash. For example, the detail view also displays data in a UITableView and if the user scrolls down in the table, an exception is thrown "-[xAccountDetailController tableView:cellForRowAtIndexPath:]: message sent to deallocated instance"

This leads me to believe that the detail controller has been deallocated properly by the popViewControllerAnimated call, but obviously something still resides in memory, if the table actually calls the cellForRowAtIndexPath, even though it's view controller has been popped off the navigationController.

Manually pushing the back button while on the Detail view (for a record that wasn't deleted on the back end) works perfectly.

Workaround attempts: As I pass in to the NSOperation a reference to the detail view controller, I have attempted to call the popViewControllerAnimated from that view's navigationController property directly and from the shared app delegate. I've also tried to pop two views and re-push the list view onto the navigationController but that presents an even more disturbing interface effect where the root and list view controllers appear to be existing simultaneously in the navigation bar (the titles overwrite each other rather than being nonexistent). I've tried to set the reference to the detail view controller that exists in the NSOperation to nil, but that does nothing. I've tried calling a [detailView release] prior to popping, but as expected, it fails on the pop because it only had a retain count of 1 prior to the release. I've also tried popToViewController and popToRootViewController and nothing seems to work. The old view is always still there, the navigation bar is the only interface element that seems to do anything, and even then, it's displaying incorrectly. I've also tried reloading the table, because I don't really mind if the view stays visible (no popping), I just want to clear the data -- but the table won't reloadData for some reason.

Thoughts: As the operationQueue that's running the NSOperation is a property of the detail view in question, I've made sure that in the detail view's dealloc to cancelAllOperations and to set the reference to the operationQueue to nil. I've also tried to make sure that all references to that view in other objects have been set to nil. Calling popViewControllerAnimated directly from a button on the detail view works perfectly, so it must be the fact that I'm calling that method from the NSOperation, running on an NSOperationQueue that's a property of the detail view I want to pop. Some reference somewhere must be keeping that view partially alive, but I can't seem to find anything that's still referencing the view when the pop is called.

There's too much code in too many places to post any here.

Any thoughts?

Sorry for such a long question and thanks in advance,

Greg


Edit 1: I can reproduce this issue outside the NSOperation with a fresh UIViewController easily.

In a tableView:didSelectRowAtIndexPath method I call the following:

xBaseViewController * vc = [[xBaseViewController alloc] initWithStyle:UITableViewStyleGrouped];
[self.navigationController pushViewController:vc animated:YES];
[vc performSelectorOnMainThread:@selector(popView) withObject:nil waitUntilDone:YES];

In vc, the popView method:

[self.navigationController popViewControllerAnimated:YES];

I don't think this has to do with retain counts or the NSOperation. Am I doing something fundamentally wrong with popping the view in this manner?

+1  A: 

Your problem is that you are trying to refresh the screen on a thread that isn't the main thread. In the iOS, you can only update what is happening on the screen with the main thread.

You need code like this:

[self performSelectorOnMainThread:@selector(mymethod) withObject:nil waitUntilDone:YES modes:nil];

And it should work just dandy.

Richard J. Ross III
Thanks for your answer. I tried this, but I can't seem to run the selector. Stepping through the debugger, it gets to this line and jumps right over it. A breakpoint in the procedure the selector calls never gets triggered. Changing the selector name to something that doesn't exist crashes the program, as would be expected, but with the right procedure name, it simply doesn't run it. Any ideas? G
Greg Herman
That has always worked for me, so I do not know what would be wrong, maybe you should take out the modes part and just use `performSelectorOnMainThread:withObject:waitUntilDone:`, but that's just one possible solution, as I cannot know what exactly you are doing inside your code.
Richard J. Ross III
Thanks again. Leaving out the 'modes:' worked. But, it unfortunately didn't solve the issue. The Back button animates to the right into oblivion, I can click on where the new back button should be and it will go back yet another view backwards, but the navbar is blank and the deallocated view is still there. G
Greg Herman
Odd, I don't know what is going on. Maybe It would be best if you found a new way to accomplish what you need to do, and not have the code try and back out of a screen.
Richard J. Ross III
I honestly don't know what the problem is, so maybe there is a retain count problem in your code. Try (in XCode) build an analyze for any errors related to memory management.
Richard J. Ross III
More info: On device (iOS 4.01) it works perfectly the first three times I do it. Afterwards, it never works correctly again. Very strange. Thanks for your help, just the same. Greg
Greg Herman