iPhone4 EXC_BAD_ACCESS for UIView after dealloc. How to debug? - exception

I'm working with Apple's Accelerometer Graph Example:
http://developer.apple.com/library/ios/#samplecode/AccelerometerGraph/Introduction/Intro.html
I'm pushing 2 Graph Views onto a navigation controller:
GraphViewController* graphViewController = [[GraphViewController alloc]initWithNibName:#"GraphViewController" bundle:nil];
[self.navigationController pushViewController:graphViewController animated:YES];
[graphViewController release];
The graph's are updated by an external method:
[motionManager startDeviceMotionUpdatesToQueue:motionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
...
if(graphDelegate)
{
[self performSelectorInBackground:#selector(notifyGraphDelegateWithMotionEvent:) withObject:motion];
}
}
, which calls
[unfiltered addX:filteredValue y:unfilteredvalue z:10];
for each graph. The frequency of updates is 20 times per second
When I pop the view from the navigation controller, I get EXC_BAD_ACCESS after [super dealloc]
-(void)dealloc
{
// Since 'text' and 'current' are weak references, we do not release them here.
// [super dealloc] will take care to release 'text' as a subview, and releasing 'segments' will release 'current'.
[segments release];
[super dealloc];
}
This is a nasty error, and I really don't know how to troubleshoot something like that. It seems to be something about the order in which the views are de-allocated, as the crash happens after the view is popped. Any ideas on how to troubleshoot something like that?

Set NSZombieEnabled, MallocStackLogging, and guard malloc in the debugger. Then, when your App crashes, type this in the gdb console:
(gdb) info malloc-history 0x543216
Replace 0x543216 with the address of the object that caused the crash, and you will get a much more useful stack trace and it should help you pinpoint the exact line in your code that is causing the problem.
See this article for more detailed instructions.

Related

Whats the best way to setup & populate my nstableview with json data?

I'm sorry if this has already been answered. I'm a bit of a noob. From all the examples I've found I can't seem to work this out.
I'm building a mac osx application using Xcode 5. My interface that I've created in Interface builder has three tableviews. My data to populate these views is json format and it is being received successfully using NSURLConnection.
What is the best way to popular the tableviews? A lot of the tutorials I've followed use an arraycontroller that is dragged and dropped using IB. From what I understand I don't need an array controller and need to hand code controllers for each of tableviews. Is that correct? If so, please explain how I go about this and setup the bindings.
Also, when clicking on a column in tableview 1 I'm planning on making it adjust the data in tableview 2 & 3 so if that impacts anything please let me know.
Thanks in advance!
Update 6 Jan
Here is my code currently..
ContactsTableViewController.h
#import <Foundation/Foundation.h>
#interface ContactsTableViewController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
IBOutlet NSTableView *contactsTableView;
}
-(id) init;
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
-(NSView *) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
#end
ContactsTableViewController.m
#import "ContactsTableViewController.h"
#implementation ContactsTableViewController
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
NSLog(#"numberOfRowsInTableView called");
return 1;
}
- (id) init {
NSLog(#"init called");
[contactsTableView setDelegate:self];
[contactsTableView setDataSource:self];
[contactsTableView reloadData];
return self;
}
- (void) initme {
NSLog(#"customfunc");
}
-(NSView *) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSLog(#"viewForTableColumn called");
NSTextField *result = [tableView makeViewWithIdentifier:#"name" owner:self];// result is now guaranteed to be valid,
result.stringValue = #"My Name"; //[self.nameArray objectAtIndex:row];
return result;
}
#end
In my appDelegate:
- (void) awakeFromNib {
NSLog(#"AWAKE");
ContactsTableViewController *contactsController = [[ContactsTableViewController alloc] init];
}
Currently nothing is showing in the tableview and numberOfRowsInTableView & viewForTableColumn aren't getting called.
Look at the UITableViewDataSource, you need to implement the protocol in one of your classes, probable your view controller, implement the UITableViewDelegate to get row selection events in the same class.

SBJsonParser: memory keeps increasing

I'm using SBJsonParser on a project of mine(due to the fact that I have to support Tiger). I'm using the version 3.0.4(it's the last non-ARC version). When I call this everything goes alright:
SBJSON *parser = [[[SBJSON alloc] init] autorelease];
The problem is when I call this:
NSDictionary *dict = [parser objectWithString:[arr objectAtIndex:i]];
Memory goes up and never goes down. If I release the dict, the app crashes. Does anyone know what could be wrong?
Thanks!

Segue not transitioning to next view?

I have been trying to figure this out for well over 3 hours now. After I successfully authorize my login with facebook, when the view tries to transition to the next view, it crashes with sigbart error: [4923:c07] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key SegueToScene1.'
This is my segue to scene:
- (IBAction)loginButtonTouchHandler:(id)sender {
// Set permissions required from the facebook user account
NSArray *permissionsArray = #[ #"user_about_me", #"user_relationships", #"user_birthday", #"user_location"];
// Login PFUser using facebook
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
[_activityIndicator stopAnimating]; // Hide loading indicator
if (user.isNew) {
NSLog(#"User with facebook signed up and logged in!");
[self performSegueWithIdentifier: #"SegueToScene1"
sender: self];
} else {
NSLog(#"User with facebook logged in!");
[self performSegueWithIdentifier: #"SegueToScene1"
sender: self];
}}];
[_activityIndicator startAnimating]; // Show loading indicator until login is finished
}
My question is, why does xcode keep refusing the segue?
Edit: I deleted my segue and put a new one "SegueMain", then changed the code to reflect that, but strangely, it still returns the same error with SegueToScene1. How strange is this? There is no trace of that title left...anywhere. Yet, it remains...
I imagine you have something connected in interface builder to perform the SegueToScene1 segue. To find it you could try searching the plain text version of your storyboard or you should probably have an idea of where it would be.

What is the proper way to unload views in iOS 6 in a memory warning (Apple doc flaw)?

In iOS 6, viewWillUnload and viewDidUnload are deprecated and UIViewControllers no longer unload views that are not visible on screen during a memory warning. The View Controller Programming Guide has an example of how to manually restore this behavior.
Here is the code sample:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Add code to clean up any of your own resources that are no longer necessary.
if ([self.view window] == nil)
{
// Add code to preserve data stored in the views that might be
// needed later.
// Add code to clean up other strong references to the view in
// the view hierarchy.
self.view = nil;
}
}
Below the code sample is the following note:
The next time the view property is accessed, the view is reloaded
exactly as it was the first time.
There is an obvious flaw here. If a view controller that has not loaded its view receives a memory warning it will load its view in the line if ([self.view window] == nil) and then proceed to clean up and release it again. At best, this is inefficient. At worst, it makes the memory conditions worse if a complex view hierarchy and supporting data are loaded. I verified this behavior in the iOS simulator.
I can certainly code around this but seems odd for Apple docs to have such an error. Am I missing something?
The correct check in a view controller for the view being loaded and on screen is:
if ([self isViewLoaded] && [self.view window] == nil)
My full solution in iOS 6 to have a view controller unload views and cleanup similar to iOS 5 is the following:
// will not be called in iOS 6, see iOS docs
- (void)viewWillUnload
{
[super viewWillUnload];
[self my_viewWillUnload];
}
// will not be called in iOS 6, see iOS docs
- (void)viewDidUnload
{
[super viewDidUnload];
[self my_viewDidUnload];
}
// in iOS 6, view is no longer unloaded so do it manually
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self isViewLoaded] && [self.view window] == nil) {
[self my_viewWillUnload];
self.view = nil;
[self my_viewDidUnload];
}
}
- (void)my_viewWillUnload
{
// prepare to unload view
}
- (void)my_viewDidUnload
{
// the view is unloaded, clean up as normal
}

UIViewController.navigationController becomes nil

I bumped into a problem where UIViewController.navigationController becomes nil and I'm desperately trying to find an answer to this one.
The UINavigationController gets setup in the application delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.rootViewController = [[RootViewController alloc] initWithNibName:#"RootView" bundle:nil];
UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:self.rootViewController];
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}
When the RootViewController is appearing, the self.navigationController member is set and I can use it to hide the navigation bar, like so:
- (void)viewWillAppear:(BOOL)animated {
NSLog( #"self = %#, self.navigationController = %#", self, self.navigationController );
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
The debug output shows values for self and self.navigationController.
When a button is clicked in this controller, self remains the same value indeed but self.navigationController is now nil:
- (IBAction)buttonClicked:(id)sender {
NSLog( #"self = %#, self.navigationController = %#", self, self.navigationController );
// here, self.navigationController is nil, so
// [self.navigationController pushViewController:...] doesn't work :-(
}
I've seen dozens of questions regarding this problem and the answer is always that the UIViewController is not part of a UINavigationController. Since accessing the navigationController in viewWillAppear works fine, I believe something else must be going on. Do you have any pointers? I'll happily provide more detail if necessary.
Try this in the app delegate:
[(UINavigationController *)self.window.rootViewController pushViewController:yourViewController animated:NO];
the rootviewcontroller is actually UINavigationController if you po to debug it. This works for me.
Your code shows that you are only using the navigationController's view but just pray that navigationController life is handled by some magic hand which is not the case.
You need someone to be the explicit owner of the navigationController here.
In fact, the following line:
[self.window addSubview:navigationController.view];
seems to indicate that what you want is for the window's rootViewController to be navigationController:
self.window.rootViewController = navigationController;
But also, it seems that the application's delegate is to be an owner of navigationController as well so navigationController should, in fact, be an ivar of your app's delegate.
In short, fix your object graph (and it will coincidentally do the extra retain you manually did and fix your bug)
I had a problem with a nil view controller and found that it was not connected properly in storyboard to the app delegate.
As always, it helps to formulate the question just to find the solution some minutes later.
I fell prey to ARC, I guess. Once I retained the UINavigationController in the application delegate, it worked fine.