App crash on iOS7 but runs fine with iOS6 - crash

While troubleshooting a crash that was only happening on iOS7 (iOS6 was fine), the application crashed several times on a line that wasn't helping with the troubleshooting, but eventually crashed once on a line like [object release].
After I removed the offending line, the crash disappeared.
Old code:
[imageView removeFromSuperview];
[imageView release];
imageView = nil;
imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
New code:
[imageView removeFromSuperview];
//[imageView release];
imageView = nil;
imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
The stack was showing the crash here: objc_msgSend ()
I would appreciate if someone could explain why it was crashing on iOS7 with the old code and what I was doing wrong?
Also I'd like to understand why it was not crashing with iOS6.

Try enabling NS Zombies to check that your aren't trying to over release it which it sounds like you are.. You should do a check to see if the imageView isn't nil before releasing it :)
I would recommend moving to ARC if you can though.

Related

ARC UIViewController not being dealloc-ed when popped from stack

I have a navigation-based app and I use MBProgressHud like crazy in my app. I converted to ARC when it came out and I experienced a lot of app crashes and I could not understand why. I began using analyzer tools and saw that my memory consumption was through the roof. I went into all of my view controllers and overloaded their dealloc methods to include a write out to NSLog to tell me if they were being dealloced or not. To my surprise, none of the ViewControllers that used MBProgressHUD were being dealloced.
I am calling MBProgressHUD like this:
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Retrieving Signature";
[HUD showWhileExecuting:#selector(getSignature) onTarget:self withObject:nil animated:YES];
When done with MBProgressHUD, you need to clean up HUD by setting the delegate to Nil, removing from superview and finally setting the HUD to nil.
HUD.delegate = nil;
[HUD removeFromSuperview];
HUD=nil;

performSelectorInBackground causes random crash when view is dismissing

I'm having some random crashes at this part of my code:
-(void) goBack {
[self performSelectorInBackground:#selector(addActivityIndicator) withObject:nil];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)addActivityIndicator {
#autoreleasepool {
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
UIBarButtonItem * barButton = [[UIBarButtonItem alloc] initWithCustomView:activityView];
[activityView startAnimating];
self.navigationItem.leftBarButtonItem = barButton;
}
}
When I want to exit the screen where these method exists, the other ViewController have to process some data. To inform the user that processing is occurring I add an activity indicator to the left button in my navigation bar.
The problem is that sometimes I get an exc_bad_access in addActivityIndicator method. The frequency is very random, sometimes the XCode shows the error at the end of #autoreleasepool, sometimes at the line self.navigationItem.leftBarButtonItem = barButton;
I imagine that sometimes my viewController is destroyed but the thread is still running and try to access the navigationItem of a object that don't exists anymore. But I'm not sure if that is the problem and I don't know how to fix it.
I'm using ARC in my project and this problem occurs in all iOS versions that I tested.
Please, anyone can explain me what is happening and how can I fix this?
Thanks.
You should never do UIKit stuff in the background.
By calling [self performSelectorInBackground:#selector(addActivityIndicator) withObject:nil]; you are updating the UI on a background thread. You should only ever update the UI on the main thread.
Edit
Based on your comment you are trying to have the UI update before the view pops. The way to do that would be:
[self addActivityIndicator]
[navigationController performSelector:#selector(popViewControllerAnimated:) withObject:[NSNumber numberWithBool:YES] afterDelay:0];
You could also look into dispatch_after

UIPopover on ARC

I am using ARC on an iPad app with the code below, the popover flashes on the screen, but doesn't stay.
What I am doing wrong?
Please help
- (IBAction)photoLibraryAction:(id)sender
{
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker setDelegate:self];
UIPopoverController *pop1 = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
[pop1 setDelegate:self];
[pop1 presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[pop1 setPopoverContentSize:CGSizeMake(320, 400)];
}
if ([pop1 isPopoverVisible])
{
// Popover is not visible
[pop1 dismissPopoverAnimated:YES];
}
}
In ARC, pop1 will be released right after -photoLibraryAction: returns, because ARC doesn't know that -presentPopoverFromBarButtonItem:permittedArrowDirections: makes the object usable beyond its scope.
You'll have to add an instance variable for your popover controller so ARC doesn't release it. Your if-statement is invalid, too, because when the method returns, pop1 is no longer available for you to use. You'll have to use an instance variable there as well.

Memory Crash in UIPopoverController

I've now invested days in trying to figure out what is going on and for the life of me I can't see what I am doing wrong. I am popping up a UIPopover when the user touches a point on the screen. The popover has a tab controller and table view that displays information about that point. But when the popover is dismissed, it crashes claiming that:
-[UIAnimator removeAnimationsForTarget:]: message sent to deallocated instance
Here is the code that loads the view controller:
MyViewController *popView = [[MyViewController alloc] init];
myPop = [[UIPopoverController alloc] initWithContentViewController:pop];
[popView release];
myPop.delegate = self;
[airportPop setPopoverContentSize:popView.view.frame.size];
[airportPop presentPopoverFromRect:CGRectMake(location.x,location.y,1,1) inView:self.mainView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
- (void)dismissPopover {
if( myPop != nil ) {
[myPop dismissPopoverAnimated:YES];
[myPop.delegate popoverControllerDidDismissPopover:airportPop];
}
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
[myPop release];
myPop = nil;
}
The actual MyViewController is just a UIViewController that with (abridged for brevity) init:
- (id)init
{
self = [super init];
//create a newview
self.view = popView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, POPUP_WIDTH, POPUP_HEIGHT)];
[popView release];
topBar = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, POPUP_WIDTH, 30)];
....
[popView addSubview:topBar];
[topBar release];
//create a table view
self.table = [[UITableView alloc] initWithFrame:CGRectMake(0, 30, POPUP_WIDTH, POPUP_HEIGHT-30-49)];
table.delegate = table.dataSource = self;
....
//create a tab bar
tabBar = [[UITabBar alloc] initWithFrame:CGRectMake(0, POPUP_HEIGHT-49, POPUP_WIDTH, 49)];
tabBar.delegate = self;
[popView addSubview:tabBar];
[popView addSubview:table];
[tabBar release];
[table release];
return( self );
}
Dealloc is nothing more than [super dealloc] since everything is essentially owned by the view and the view controller will take care of it. When the myPop is released, in DidDismissPopover, the view is also released, so that seems to work okay. But very soon thereafter, I get the crash.
Do I need to do something special to discard the tab view or table view when the popup dismisses?
I am using an autorelease on the cells in the table, should I stop doing that?
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
Thank you in advance for any help!!! Any ideas at all are greatly appreciated!!
-Kevin
[myPop dismissPopoverAnimated:YES] will continue to access you object even after the method call because you set YES for the animation (there is a timer and other stuff going under the hood to perform the animation for that)
So, instead of immediately releasing the object, you could mark it as autorelease to postpone this action, which actually might solved it or not.
Or postpone the release to a time after that makes tyou sure thta the animation will be finished. You could use GCD for that (if you are using iOS 4+) and as the default time for animation in UIKit is 0.3s, the code bellow should do the trick.
double delayInSeconds = 0.3;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[myPop.delegate popoverControllerDidDismissPopover:airportPop];
});
EDIT: You should use this time only for the test propose as it is far from being the right way to release an object.
You should store a pointer to your UIPopover and release it in your class dealloc method.
Add following keys in yor Exectables info->Arguments tab-> enviroment variables
NSZombieEnabled = YES
CFZombie = 5
MallocStackLoggingNoCompact = 1
then when you get crash automatically you get a message
some thing like this
(gdb) continue
2011-06-09 11:46:08.404 test [6842:40b] * -[_NSArrayI
release]:message sent to deallocated instance 0X64a4900
then type
(gdb) info malloc-history 0x64a4900
it will give you complete history.
May be it helps you to find the place.
also you can use where command when you got crash.
The fastest way to avoid waiting for animation to end is to set popoverController.delegate = nil as soon as you dismiss the popup or the Popover Delegate method
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
is called.

Using Instruments to improve memory-management with modal view controllers

I feel like I don't understand something fundamental here. I've been working on memory management in my app while using Instruments to check out live allocations. I have a modal view controller (settingsViewController) that has an image for a background. One thing I noticed was that even after settingsViewController dealloc is called, there still is a live Malloc 520 KB, ImageIO is the responsible library. I'd expect live memory to drop back down after dismissing settingsViewController. Is the UIImageView still hanging around somewhere?
Here is how I load the image in viewDidLoad, as well as dismiss the view controller when I'm finished.
- (void)loadView {
[super loadView];
////// background ////////
UIImageView *background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"settings_background.png"]];
[self.view addSubview:background];
[background release];
//////////////////////////
}
- (void)viewDidLoad {
[super viewDidLoad];
///////// done button //////////
UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneButtonPressed)];
self.navigationItem.leftBarButtonItem = done;
[done release];
////////////////////////////////
}
- (void) doneButtonPressed {
[self dismissModalViewControllerAnimated:YES];
}
- (void)dealloc {
NSLog(#"settingsViewController dealloc"];
[super dealloc];
}
At this point, this is all that is in the settingsViewController, so no need to do anything special in dealloc, right? Here is how I'm showing testViewController in the rootViewController.
- (void) loadSettingsView {
SettingsViewController *settingsViewController = [[SettingsViewController alloc] init];
UINavigationController *settingsNavigationController = [[UINavigationController alloc] initWithRootViewController:settingsViewController];
[self presentModalViewController:settingsNavigationController animated:YES];
[settingsViewController release];
[settingsNavigationController release];
}
I'd like to make sure I understand what is going on before moving forward. I have several different modal view controllers, each with a different image as a background. Since each one creates a Malloc 520 KB, I end up using 2-3 MB of precious memory for no good reason. What is holding on to that 520 KB?
When you use the +[UIImage imageNamed:] method, the framework caches the image data for you. That's why you see it hold onto some memory even after your view is released. If you're working in the simulator and you want to see it release that memory, send the simulator a memory warning after you've dismissed your view. The image framework should then release the cached image data.