iPhone: IBAction Causes "Unrecognized Selector Sent to Instance" Error - iphone

I'm working on my first real iPhone app, a simple To-Do list application to help me organize stuff, except I'm getting an "unrecognized selector sent to instance 0x".
Specifically:
2010-02-20 14:30:09.200 ToDoApp[88562:20b] *** -[NSCFDictionary switchViews:]: unrecognized selector sent to instance 0x3d22de0
2010-02-20 14:30:09.201 ToDoApp[88562:20b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFDictionary switchViews:]: unrecognized selector sent to instance 0x3d22de0'
I've looked around and figured out that it might be a connection problem in IB, but I'm new to this whole connecting thing (man, I wish they supported Java or Python), so here's how it's laid out. I've got 3 classes, a SwitchViewController, a MainScreenViewController, and a ToDoListViewController. When I hit a button on MainScreenViewController, I trigger the "switchViews" function that's throwing this problem. They way I've got it set up is that the button (a UIBarButtonItem) has the "sentAction" go to switchViews. This ViewButton has its reference outlet as a IBOutlet in SwitchViewController.
So here's the .h for SVC:
#import <UIKit/UIKit.h>
#class MainScreenViewController;
#class ToDoListViewController;
#class EditViewController;
#define kMinimumGestureLength 25
#define kMaximumVariance 5
#interface SwitchViewController : UIViewController {
MainScreenViewController *mainScreenViewController;
ToDoListViewController *toDoListViewController;
EditViewController *editViewController;
IBOutlet UIBarButtonItem *viewButton;
CGPoint gestureStartPoint;
}
#property (retain, nonatomic) MainScreenViewController *mainScreenViewController;
#property (retain, nonatomic) ToDoListViewController *toDoListViewController;
#property (retain, nonatomic) EditViewController *editViewController;
#property (retain, nonatomic) IBOutlet UIBarButtonItem *viewButton;
#property CGPoint gestureStartPoint;
-(IBAction)switchViews:(id)sender;
And for the switchViews function:
-(IBAction) switchViews:(id)sender
{
NSLog(#"Switching views");
if(self.toDoListViewController.view.superview == nil){
if(self.toDoListViewController ==nil){
ToDoListViewController *toDoVC = [[ToDoListViewController alloc] initWithNibName:#"ToDoListView" bundle:nil];
self.toDoListViewController = toDoVC;
//[toDoVC release];
}
[mainScreenViewController.view removeFromSuperview];
[self.view insertSubview:toDoListViewController.view atIndex:0];
}
else{
if(self.mainScreenViewController == nil){
MainScreenViewController *mainController = [[MainScreenViewController alloc] initWithNibName:#"MainScreenView" bundle:nil];
self.mainScreenViewController = mainController;
//[mainController release];
}
[toDoListViewController.view removeFromSuperview];
[self.view insertSubview:mainScreenViewController.view atIndex:0];
}
}
So in short, I'm totally lost, and this is really frustrating. Anyone have any advice, or need any more code?

We just ran into the same problem. It seems we released the ViewController object in the AppDelegate and then our nib view tried to invoke an IBAction (on the view controller). Half the time we were getting "EXC_BAD_ACCESS" (aka messaging a released object), and half the time we were getting "unrecognized selector sent to instance" of NSCFString, NSCFArray, all sorts of things (aka messaging an area of memory now taken up by a different object).
Just check your ViewController hasn't been released.

Okay, got the solution pointed out to me. Should have had it routed through FirstResponder (I ... really don't understand why that works, but at this point I'm just happy it works.)
I'm not sure how first responder works anyways (none of the books I have really mention it), but it... works? If someone wants to give me a rundown, that'd be useful... but this question has been answered.

I'm going to guess the problem is in your nib file.
The error means that upon clicking the button, the button tries to send a message/method-call of switchView to an NSDictionary object which of course has no such method. The error then lays in where the buttons action is pointed.
Check the nib for this view. Look at the File Owner and check the class assigned to it. Make sure it is SwitchViewController and not a dictionary for some reason. If the File Owner property is set to a dictionary class it will load a dictionary and try to send the action method it to it.

The right answer is this:
The view controller that we assign as the first screen in the app delegate shouldn't be released in the - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method. In your case the first screen is MainScreenViewController.
It (MainScreenViewController instance)should be released in app delegate's dealloc method.
- (void)dealloc
{
[_window release];
[mainScreenViewController release];
[super dealloc];
}

This might also help. The Analyzer routine recommended I release a few objects, and I did. Turns out, I needed those objects in the app. In the xAppDelegate.m file (or whatever) in the appDidFinishLaunching method/message/function/routine/thing, use
UINavigationController *navController = [[UINavigationController alloc] init];
instead of
UINavigationController *navController = [[[UINavigationController alloc] init] autorelease];
Also, Analyzer recommended I release the object I pushed onto the nav controller. Big mistake. That file was my menu screen and when I pressed a button, I received a unrecognized selector sent to instance. Apparently, it was calling IBAction on NSString and NSDictionary, which is not good.

FYI I was getting this when using ARC and the xib was being loading and put onto the screen, but somehow the VC itself was not being retained.
I solved it by adding a variable to store reference in the VC that was presenting it.

The problem is, you have initiated the UIViewController instance as a method variable. So the view controller have no scope after the execution of the method and so it is released from the memory cycle. So you have to make your view controller instance as class level.
#interface SwitchViewController () {
ToDoListViewController *toDoVC;
MainScreenViewController *mainController;
}
-(IBAction) switchViews:(id)sender
{
if (!toDoVC)
toDoVC = [[ToDoListViewController alloc] initWithNibName:#"ToDoListView" bundle:nil];
if (!mainController)
mainController = [[MainScreenViewController alloc] initWithNibName:#"MainScreenView" bundle:nil];
//Your stuff with the view controllers...
}

Related

UITableViewController's view added as subview to UIViewController crashes app

I have these two classes in my project:
#import <UIKit/UIKit.h>
#import "TableViewController.h"
#interface MainViewController : UIViewController
#end
and
#import <UIKit/UIKit.h>
#interface TableViewController : UITableViewController
#end
Within MainViewController.m, I am trying to do this:
TableViewController *tview = [[TableViewController alloc] initWithNibName:#"TableViewController"
bundle:nil];
tview.tableView.dataSource = tview;
tview.tableView.delegate = tview;
[self.view addSubview: tview.view];
However, this is crashing with an:
-[MainViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0xb52dc90'
I don't understand why MainViewController is becoming the datasource/delegate for the TableViewController, when I already set the delegate/datasource in the init above. I also tried to set TableViewController's delegate/datasource to self from within its viewDidLoad, but the MainViewController still keeps acting as the delegate/datasource, regardless. I tried with the nib files hooked up and not hooked up, but no difference.
Ideally, I want the TableViewController to act as the delegate and datasource, and have its view added to MainViewController. How can I get this done?
Thank you!
Note: Neither solution worked for some reason. In the end, I recreated TableViewController as inheriting from UIViewController and set it as and things went normally then.
Where have you written the TableView delegate methods?? They should be inside your TableViewController Class and not inside the MainViewController Class.
Write the UITableView delegate methods inside the TableViewController Class.
Hope this will solve your problem...
Have you set delegate and datasource to nil in dealloc method?
tview.tableView.delegate = nil;
tview.tableView.dataSource = nil;

Delegate set in View Controller, not maintaining in subview

I asked this question earlier with way too much code.
The ViewController initializes a UIView chain, Controller>>View>>SubView, in the ViewController. After the SubView is initialized the ViewController is set as its delegate:
aSubView.delegate = self;
NSLog(#"$#",aSubview.delegate), returns the ViewController, so I know it is set.
In the SubView, NSLog(#"$#",self.delegate),returns random crap such a hr.lproj or a file path to the Foundation framework.
It crashes when attempting to implement any of the delegates methods, since the delegate doesn't link to the ViewController but instead randomness.
This is what the SubView.h file looks like:
#import "TestDelegate.h"
#interface TestSubView : UIView {
id<TestDelegate> delegate;
}
#property (assign) id<TestDelegate> delegate;
EDIT: ViewController is initialized in the app delegate as such:
ViewController *controller = [[ViewController alloc] init];
[window addSubview:controller.view];
[controller release];
The only other thing I added to the App Delegate, over the default is an import of the ViewController header
Is it possible the view controller object is being released/dealloced between the two calls to NSLog?

UIViewController memory management

Hi I have a very basic issue of memory management with my UIViewController (or any other object that I create);
The problem is that in Instruments my Object allocation graph is always rising even though I am calling release on then assigning them nil.
I have 2 UIViewController sub-classes each initializing with a NIB;
I add the first ViewController to the main window like [window addSubView:first.view];
Then in my first ViewController nib file I have a Button which loads the second ViewController like :
-(IBAction)loadSecondView{
if(second!=nil){ //second is set as an iVar and #property (nonatomic, retain)ViewController2* second;
[second release];
second=nil;
}
second=[[ViewController2 alloc]initWithNibName:#"ViewController2" bundle:nil];
[self.view addSubView:second.view];
}
In my (second) ViewController2 i have a button with an action method
-(IBAction) removeSecond{
[self.view removeFromSuperView];
}
Please let me know if the above scheme works in a managed way for memory...?
In Instruments It does not show release of any allocation and keeps the bar status graph keeps on rising.
First of all, why use this scheme when second is a property:
if(second!=nil){
[second release];
second=nil;
}
second=[[ViewController2* second]initWithNibName:#"ViewController2" bundle:nil];
A propery automatically releases it's old value when the setter is used. So this could be rewritten as:
if(self.second == nil) { //Prevents creating a new controller if there already is one.
self.second = [[[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil] autorelease];
}
Also, what's up with [ViewController2* second]?
Why is that asterisk there, and what does the class method second do?

Removing UITabBarController

Someone has posted a similar question to this with no resolution, but also no sample code. So, I thought I would post my problem in detail here.
I have a game with several modes of play, each of these having several options. After playing around with various designs, it seems cleanest to put them into a UITabBarController with three tabs, one for each class of games. I created a new UIVIewController which is loaded from the main menu screen (replacing the main screen) and initializes the UITabBarController as follows:
barController = [[UITabBarController alloc] init];
Games1 *vc1 = [[[Games1 alloc] initWithNibName:#"Games1" bundle:nil] autorelease];
Games2 *vc2 = [[[Games2 alloc] initWithNibName:#"Games2" bundle:nil] autorelease];
Games3 *vc3 = [[[Games3 alloc] initWithNibName:#"Games3" bundle:nil] autorelease];
NSArray* controllers = [NSArray arrayWithObjects:vc3, vc1, vc2, nil];
barController.viewControllers = controllers;
[self.view addSubview:barController.view];
When the user selects a game, I remove the UIViewController from the window and deallocate as follows:
- (void)dealloc {
printf("Games released: barController: %d\n", [barController retainCount]);
[barController.view removeFromSuperview];
barController.viewControllers = 0;
[barController release];
barController = 0;
[super dealloc];
}
The problem I have is that when I rotate the device, I get a crash. If I launch a game mode directly from the main screen and rotate, no crash. I've verified that everything is getting deallocated, and my retain count on the bar controller is 1. Any suggestions on how to eliminate this crash? Thanks!
[Edit] A bit more info:
The barController is defined as:
IBOutlet UITabBarController *barController;
with:
#property (nonatomic, retain) IBOutlet UITabBarController *barController;
It ends up the problem was only peripherally related to the UITabBarController. I was adding and removing UIViewControllers directly to my app window, which has been shown to be causing problems elsewhere. Adding a master UIViewController / UIView and only adding and removing from that fixes everything, although an autorelease instead of a release may have worked as well. See the discussion here:
View Controller being sent a message even though it has been deallocated
The UITabBarController was just causing the problem to happen much more quickly and obviously.
Don't do this:
barController.viewControllers = 0;
In -dealloc you should only remove the UITabBarController's view from its superview and release it.

How to fix: unrecognized selector sent to instance

I am having a problem that may be simple to fix, but not simple for me to debug. I simple have a button inside a view in IB; the file's owner is set to the view controller class I am using and when making the connections, everything seems fine, ie. the connector is finding the method I am trying to call etc.
however, I am receiving this error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UIApplication getStarted:]: unrecognized selector sent to instance 0x3d19130'
My code is as follows:
RootViewController.h
#interface RootViewController : UIViewController {
IBOutlet UIButton* getStartedButton;
}
#property (nonatomic, retain) UIButton* getStartedButton;
- (IBAction) getStarted: (id)sender;
#end
RootViewController.m
#import "RootViewController.h"
#import "SimpleDrillDownAppDelegate.h"
#implementation RootViewController
#synthesize getStartedButton;
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction) getStarted: (id)sender {
NSLog(#"Button Tapped!");
//[self.view removeFromSuperview];
}
- (void)dealloc {
[getStartedButton release];
[super dealloc];
}
#end
Seems simple enough...any thoughs?
It looks like you have released the RootViewController after add it to your window.
Post here the piece of code where you add RootViewController to your window.
BTW, try to comment the line where you do the release. So, instead of use:
RootViewController *viewController = [[RootViewController alloc] init];
[window addSubview:viewController.view];
[viewController release];
Do it:
RootViewController *viewController = [[RootViewController alloc] init];
[window addSubview:viewController.view];
//[viewController release];
Your method "getStarted" should work after that.
Cheers,
VFN
You're sending getStarted to UIApplication and not RootViewController. Double check to make sure the button is hooked up properly to the view controller in Interface Builder. Your code looks fine.
I have the same problem, and finally found the answer at this forum post:
http://iphonedevbook.com/forum/viewtopic.php?f=25&t=706&start=0
When you instantiate your view controller class to be pushed into the navigation controller or similar, you have to make sure you instantiate it with your custom class name, and not UIViewController. This is easy to miss, and difficult to debug.
For example:
// this is wrong
RootViewController *viewController = [[UIViewController alloc] initWithNibName:#"TestView" bundle:nil];
// It should be
RootViewController *viewController = [[RootViewController alloc] initWithNibName:#"TestView" bundle:nil];
Hope it helps!
PS: Oops. Just noticed that your error says UIApplication, and not UIViewController, unlike mine. So probably like what Marc W said, somewhere you have used UIApplication where you should have used your custom class. It is probably something like my mistake.