How to end a view controller totally? - viewcontroller

So here is the issue. When I start an application, I start retrieving data from my server.
I have a delegate method with locationManager which detects a change in GPS location.
-(void) latLocation:(double)lat lonLocation:(double)lon{
//Codes to retrieve data from server.
NSLog(#"Retrieving...");
}
The following methods will run the moment a location is updated.
The issue here is, I have used this method in my launch screen which is only supposed to run ONCE. I have used:
[self.navigationController popViewController];
after finishing my data retrieval.
But it appears my view is still running as the method keeps on calling over and over as my NSLog constantly prints over and over again.

I seem to have solved it by simply niling the delegate.
locationManager.delegate = nil;

Related

iPhone Initial Data Load and 'Please Wait' Screen

I am updating a rather straightforward iPhone app that requires an initial data load into a SQLite databbase upon initial setup. I am using the DSActivityView class to provide a nice "Please wait" message. The entire data load takes about 15 seconds on WiFi and about 30-45 seconds on 3G.
This whole process originates in the application:didFinishLaunchingWithOptions method of my app delegate.
The data load procedures are being launched in a separate thread by creating an instance of an NSOperation object called UpdateTable for each of the 8 tables. Each operation is placed in an NSOperationQueue, and then released. The data loading works like a champ.
What is NOT working like a champ; however, is the replacement of the "Please wait.." view with the main navigation controller view.
The first approach was to instantiate an NSInvocationOperation that called a method in my app delegate that to execute the following:
- (void) loadNavController;
{
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
[defaultImageView removeFromSuperview];
[DSBezelActivityView removeViewAnimated:YES];
}
This operation was added to the queue after the last UpdateTable operation was added.
The reason this approach is buggy is because the NSInvocationOperation runs concurrent with the other processes in the thread; therefore, the method shown above fires before the last table update can be performed.
So I tried the following approach:
In my UpdateTable.m (where all of the JSON and SQLite is taking place), I entered the following line to execute immediately after the last table update completed:
[appDelegate performSelectorOnMainThread:#selector(loadNavController) withObject:nil waitUntilDone:NO];
This approach got the timing right, however, the main UNavigationController and UIWindow objects were both nil with loadNavController executed. Setting some debug breaks, I noticed that the delloc method of my app delegate was firing prior to the loadNavController method was executed. Both objects are assigned to the MainWindow via IBOutlets that are set up as IBOutlets (nonatomic, retain) in the App Delegate. I haven't a clue as to why the dealloc is firing, as this process is taking less than 10 seconds in total.
Any assistance you might render would be greatly appreciated. Many thanks in advance.
VB
A quick note to update this thread with the solution.
The performSelectorOnMainThread approach turned out to be correct. The reason the navController object was nil when the selector was called was due to me creating a new instance of the app delegate:
AppDelegate *appDelegate = [AppDelegate alloc] init];
instead of referencing the existing object:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
Works like a charm.

iOS check if delegate exists before call method

I write iOS app and use imageStore library to lazy load images and cache them in memory. (https://github.com/psychs/imagestore)
On ViewController I create imagestore instance:
imageStore = [ImageStore new];
imageStore.delegate = self;
When image loaded successfuly, imagestore call delegate method
- (void)imageStoreDidGetNewImage:(ImageStore*)sender url:(NSString*)url
that doing reloadData on tableview to redraw cells.
All works good. But there is the problem: if ViewController didUnload (go back in navigation controller) and image loaded, application finish with crash, because imagestore call method of unloaded ViewController.
I try to do following:
1) in ViewController I place this code in viewDidUnload section:
imageStore.delegate = nil;
imageStore = nil;
2) In imageStore I added checking for nil:
if(delegate != nil) {
...call delegate method
}
It works, but periodically app crash anyway.
Try putting this code on dealloc section.
imageStore.delegate = nil;
imageStore = nil;
In the same way the if clause is not necessary because any call to an nil object is ignored by the application, so if you have something like this:
id delegate = nil;
[delegate callAnyMethod];
has no effect in your application behavior, in other hand if the call of the method delegate is optional you should asure that delegate responds to selector, something like this should do the trick:
if([delegate conformsToProtocol:#protocol(yourProtocolName)] && [delegate respondsToSelector:#selector(imageStoreDidGetNewImage:url:)]) {
[delegate imageStoreDidGetNewImage:imageStore url:url];
}
Cheers!
It works, but periodically app crash anyway.
That's a contradiction. There are two possibilities:
Your fix worked, and the app is crashing for some other reason.
Your fix did not work, the app continues to crash for the same reason it was crashing before.
It's hard to know what's wrong without knowing which of these two possibilities is in fact happening. Look at the error message and the evidence from the crash, such as the stack crawl. Why is the app crashing? Does it try to dereference the delegate property somewhere without checking it first? Does it depend on the delegate doing something, so that if the delegate no longer exists that thing doesn't get done and that in turn leads to a crash? These are the kinds of things I'd look for, but again the most important thing is to start with the evidence you have and follow your nose.

Xcode4, iOS: Certain parts of instance method being ignored, no errors, just passed by

new iOS guy here. I have a problem that Googling and searching on here has not shed any light on. I'm assuming this is basic. I have a simple app (app delegate and 1 view controller), and as part of it I'm using local notification. So, in the app delegate I use the 'didReceiveLocalNotification' to watch for the notifications. Depending on which one comes in, I then call one of several instance methods in my main view controller.
ie in the AppDelegate.m
- (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification {
MyViewController* controller = [[MyViewController alloc] autorelease];
if ([[notification.userInfo objectForKey:#"id"] isEqualToString:#"myKey"]) {
[controller checkActive];
}
}
Through logging and watching some breakpoints, this is all working. If the app is in the background, the notification comes in, app opens, and the correct instance method is called.
What I cannot figure out at all is why some parts of the instance method are simply being passed by, with no effect. For a simple example, if we have this:
-(void)checkActive {
ViewThing.alpha = 1.0;
NSLog(#"checkActive ran");
}
The log statement will show up fine, but the ViewThing will not change. Elsewhere in the main view controller I'm calling the same checkActive method with no problems and it changes the ViewThing. (via another interface button IBAction method in that case).
There are no errors, no warnings, the console shows nothing, putting a breakpoint on ViewThing shows that it hits the line. I'm stumped, cannot see what is different from trying to calling the method from the delegate vs. on an IBAction.
Thanks for any tips!
If the alpha is not correctly changing there a few possible issues with 1 and 2 being the most likely.
ViewThing is nil. Reasons could be is the view unloaded and you set it to nil or checkActive was called before the outlets were set.
ViewThing.alpha is being set on a thread that is not the main thread. Attempting to change UI elements on a separate thread will caused undefined behavior such as never updating the change or taking an extended amount of time to update the change. You can check if it is the main thread using [NSThread isMainThread].
ViewThing is pointing a different view.
1 & 2 can easily be checked by logging view
NSLog(#"checkActive ran %#", ViewThing);

iPhone app crashes when user exits UITableView with MBProgressHUD running in separate thread

In my app, I have a UITableViewController which loads calculated data from my Core Data store. This can take quite some time and hangs the main thread, so to give the user visual feedback, I have installed the MBProgressHUD widget which will show a progress indicator (just the spinning wheel) while the calculation method runs in a separate thread.
This is fine, except that the user can still exit the UITableViewController if they think it's taking too long. Of course, this is a good thing, except that when the separate thread concludes its operation, it still tries to call its delegate (the UITableViewController) to hide the MBProgressHUD. This causes a crash because since the user already exited the UITableViewController, it has dealloc'd and release'd itself.
MBProgressHUD has the following code to try to stop this:
if(delegate != nil && [delegate conformsToProtocol:#protocol(MBProgressHUDDelegate)]) {
if([delegate respondsToSelector:#selector(hudWasHidden)]) {
[delegate performSelector:#selector(hudWasHidden)];
}
}
However, my app somehow seems to still be running this inner code ([delegate performSelector:#selector(hudWasHidden)]) even though the UITableViewController is totally gone--causing the app to crash.
Any suggestions? I am not running with NSZombiesEnabled.
From your UITableViewController viewWillDisappear, viewDidDisappear or dealloc method, set the MBProgressHUD.delegate = nil;
Once the user has exited the table controller, the delegate property of the hud points to a deallocated object (= a memory zone that could contain anything). That causes the crash when the computing thread ends and tries to send any message to the delegate.
In your table view controller dealloc, you must set the hud delegate to nil.

Stop lazy-loading images?

Here's the issue – I followed along with the Apple lazy-load image sample code to handle my graphical tables. It works great. However, my lazy-load image tables are being stacked within a navigation controller so that you can move in and out of menus. While all this works, I'm getting a persistent crash when I move into a table then move immediately back out of it using the "back" button. This appears to be a result of the network connections loading content not being closed properly, or calling back to their released delegates. Now, I've tried working through this and carefully setting all delegates to nil and calling close on all open network connections before releasing a menu. However, I'm still getting the error. Also – short posting my entire application code into this post, I can't really post a specific code snippet that illustrates this situation.
I wonder if anyone has ideas for tasks that I may be missing? Do I need to do anything to close a network connection other than closing it and setting it to nil? Also, I'm new to debugging tools – can anyone suggest a good tool to use to watch network connections and see what's causing them to fail?
Thanks!
Have you run it through the debugger (Cmd-Y)? Does it stop at the place where the crash is happening? That should show you in code where the issue is happening. I'm betting the issue has to do with over-releasing something rather than cleaning up connections. Are you getting EXC_BAD_ACCESS? Check any delegates and make sure they are nil when -viewWillDisappear gets called. That way, if anything tries to call back to a delegate, it will just be a no-op.
You may also want to try enabling zombies (NSZombieEnabled) which will tell you when an object that has been released is being accessed again. It's very helpful in finding over-released objects.
Ah ha... after a large zombie hunt (thanks, Matt Long), I discovered that the issue stems from an error in Apple's LazyTableImages sample code. That example provides the following implementation for canceling all image loads, which I turned into a general-purpose stopAllImageLoads method...
From RootViewController.m in LazyTableImages sample code:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// terminate all pending download connections
NSArray *allDownloads = [self.imageDownloadsInProgress allValues];
[allDownloads performSelector:#selector(cancelDownload)];
}
There is in error in the last line of the above method where performSelector is called on an array of objects. The above implementation calls the selector on the array itself, rather that on each object in the array. Therefore, that last line should be this:
[allDownloads makeObjectsPerformSelector:#selector(cancelDownload)];
Once that line was changed, everything else fell into place. It turns out I wasn't calling my stopAllImageLoads method where I meant to – I had disabled it at one point because it was causing an error. Once that was back in place, the memory issues cleared up because image loads were successfully canceled before the table delegate was released.
Thanks all for your help.
If you're doing ANY asynchronous function (network requests, Core Location updates, etc), you run the risk that your view controller that is the delegate of that action is deallocated by the time the async function returns. i.e. you back out of the view and take the delegate target away from the background process. I've dealt with this several times.
Here's what you do. Use the ASIHTTPRequest library (which you should be doing anyway--it's brilliant). Create a synthesized property to hold your request. Then in viewWillDisappear, call -cancel on your request. To be safe, I also set its delegate to nil, but that should be unnecessary.
Here's a sketch of what you want to do. Note I typed this right here, haven't syntax-checked it or anything.
#implementation MyViewController
#synthesize req //this is an ASIHTTPRequest *req.
-(void)viewDidLoad
{
//make an NSURL object called myURL
self.req = [ASIHTTPRequest requestWithURL:myURL];
self.req.delegate = self;
[self.req startAsynchronous];
}
-(void)viewWillDisappear
{
[self.req cancel];
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSString *string = [request responseString];
}