I'm developing an iOS 4 application using latest SDK, XCode 4.2 and ARC.
I've added a method to appDelegate.h
#import <UIKit/UIKit.h>
#class ViewController;
#class SecondViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
UINavigationController* navController;
ViewController* viewController;
SecondViewController* secondViewController;
}
#property (strong, nonatomic) UIWindow *window;
- (void) showSecondViewController;
#end
And it's implemented in appDelegate.m
#import "AppDelegate.h"
#import "ViewController.h"
#import "SecondViewController.h"
#implementation AppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
viewController.title = #"First";
navController = [[UINavigationController alloc] initWithRootViewController:viewController];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
...
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
...
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
...
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
...
}
- (void)applicationWillTerminate:(UIApplication *)application
{
...
}
- (void) showSecondViewController
{
secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondViewController.title = #"Second";
[navController pushViewController:secondViewController animated:YES];
}
#end
But, when I send a message to that method in ViewController.m
- (IBAction)goSecondClicked:(id)sender
{
[[[UIApplication sharedApplication] delegate] showSecondViewController];
}
I get the following compiler error:
Automatic Reference Counting Issue
No known instance method for selector 'showSecondViewController'
Any clue?
You will need to cast the delegate object that you get as:
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Then call the method on appDelegate
Change your goSecondClicked action method to this:
- (IBAction)goSecondClicked:(id)sender
{
[[[UIApplication sharedApplication] delegate] performSelector:#selector(showSecondViewController)];
}
EDIT: although this alternative works for the given situation, it should be noted that the compiler won't help you if you change the method name in your delegate and forget to change the name on the selector call. So, this should be used carefully.
You can also define this macro on your AppDelegate.h
#define APP_DELEGATE (AppDelegate *)[[UIApplication sharedApplication] delegate]
After this, you can invoke your selector with:
[APP_DELEGATE showSecondViewController];
Related
Why am I allowed to access orientationChanged: without declaring it in the .h file? I couldn't find the method in Apple's documentations, so I don't think it's an inherited method.
#import <UIKit/UIKit.h>
#interface RotationAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
#import "RotationAppDelegate.h"
#implementation RotationAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIDevice *device = [UIDevice currentDevice];
[device beginGeneratingDeviceOrientationNotifications];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:device];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
-(void)orientationChanged:(NSNotification *)note {
NSLog(#"oriendtationChanged: %i", [[note object] orientation]);
}
Methods that you pass to #selector(...) do not need to be declared in a header file. In fact, they do not even need to exist, as long as nobody tries to execute them at runtime. A string orientationChanged: is perfectly sufficient for the #selector(...) to produce a valid selector for you; as long as the corresponding method is available at run-time (i.e. the object's respondsToSelector: returns YES for it), the system will find and execute it correctly.
I am using Apple's newest Xcode for MacOS 10.7(Lion). I am trying to make a iPhone application. I'm new to the language and decided to load up apples guides. 'Your first iOS application'. It was good, taught me a few things but Its not working. I get Expected Getter Method Not Found On Object Of Type 'TestAppDelegate *'
How do i Fix this?
Heres the code:
TestAppDelegate.m
#import "TestAppDelegate.h"
#import "MyViewController.h"
#implementation TestAppDelegate
#synthesize window=_window;
#synthesize myViewController=_myViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
MyViewController *aViewController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
self.myViewController = aViewController;
// Or, instead of the line above:
// [self setMyViewController:aViewController];
[aViewController release];
self.window.rootViewController = self.MyViewController;
[self.window makeKeyAndVisible];
return YES;
}
/* Other methods omitted from the listing. */
- (void)dealloc {
[_myViewController release];
[_window release];
[super dealloc];
}
#end
The line:
self.window.rootViewController = self.MyViewController;
Has the problem
objective-c is case-sensitive. You wrote an upper-case M instead of lower-case.
self.window.rootViewController = self.myViewController;
Your window doesn't exist yet (at least from what I can see in your code). Add this before the line causing the SIGABRT:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
I'm really new to iPhone programming.
This app is a simple quiz. FirstAppDelegate.m creates an instance of QuizViewController and adds its view to the window.
#import "FirstAppDelegate.h"
#import "ResultViewController.h"
#import "QuizViewController.h"
#implementation FirstAppDelegate
#synthesize window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {
UIViewController *vc = [[QuizViewController alloc] init];
[window addSubview:[vc view]];
[window makeKeyAndVisible];
[vc release];
return YES;
}
- (void)dealloc {
[window release];
[super dealloc];
}
#end
I thought I could release vc like I do hear since window will retain it (?) but it generated an error :
2011-06-28 23:06:34.190 First[14289:207] -[__NSCFType foo:]: unrecognized selector sent to instance 0x4e1fc90
2011-06-28 23:06:34.193 First[14289:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType foo:]: unrecognized selector sent to instance 0x4e1fc90'
...so I commented it and now it works fine. But where should I release vc? Here's QuizViewController.h:
#import <UIKit/UIKit.h>
#interface QuizViewController : UIViewController {
IBOutlet UILabel *questionLabel;
IBOutlet UIButton *button1;
IBOutlet UIButton *button2;
IBOutlet UIButton *button3;
int currentQuestionIndex;
int corrects;
NSMutableArray *questions;
NSMutableArray *answers;
NSMutableArray *correctAnswers;
}
- (IBAction)foo:(id)sender;
#end
...and QuizViewController.m:
#import "QuizViewController.h"
#implementation QuizViewController
- (id)init {
NSLog(#"QuizViewController init");
[super initWithNibName:#"QuizViewController" bundle:nil];
questions = [[NSMutableArray alloc] init];
answers = [[NSMutableArray alloc] init];
correctAnswers = [[NSMutableArray alloc] init];
[questions addObject:#"Vad betyder det engelska ordet \"though\"?"];
[answers addObject:#"Tuff"];
[answers addObject:#"Dock"];
[answers addObject:#"Tanke"];
[correctAnswers addObject:#"Dock"];
[questions addObject:#"Vad hette frontpersonen i Popbandet Queen?"];
[answers addObject:#"Pierre Bouviere"];
[answers addObject:#"Freddie Mercury"];
[answers addObject:#"Stevie Wonder"];
[correctAnswers addObject:#"Freddie Mercury"];
return self;
}
- (IBAction)foo:(id)sender {
NSLog(#"foo");
}
- (void)loadView {
NSLog(#"QuizViewController loadView");
[questionLabel setText:[questions objectAtIndex:currentQuestionIndex]];
[button1 setTitle:[answers objectAtIndex:currentQuestionIndex] forState:UIControlStateNormal];
[button2 setTitle:[answers objectAtIndex:currentQuestionIndex + 1] forState:UIControlStateNormal];
[button3 setTitle:[answers objectAtIndex:currentQuestionIndex + 2] forState:UIControlStateNormal];
[super loadView];
}
- (void)viewDidLoad {
NSLog(#"QuizViewController viewDidLoad");
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
}
#end
You should create an instance variable to hold the VC. The reason you are losing it when you release it is that the window is only retaining the view and not the controller.
I thought I could release vc like I do hear since window will retain it...
Notice that you are adding the view associated to the view controller ([vc view]) to your UIWindow. That object will be retained, not your controller.
You can fix this by defining a variable in your FirstAppDelegate to store the controller there and release it in FirstAppDelegate dealloc.
#interface FirstAppDelegate
.....
#property (nonatomic, retain) QuizViewController* controller;
.....
#end
#implementation FirstAppDelegate
#synthesize window;
#synthesize controller;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {
self.controller = [[QuizViewController alloc] init] autorelease];
[window addSubview:[vc view]];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
....
[controller release]; controller = nil;
....
}
The views/windows do retain their child views, the view controllers retain their views, but the views don't retain their controllers. It's a "one-way" relationship, a clear has-a. This also comes in handy to prevent retain cycles.
You probably want to save the controller in an ivar in the class you alloc/init it, and release it in dealloc or when you pull the view from screen.
View controllers often get retained by other view controllers, i.e. when you push them onto a navigation stack, or put them in tabs.
If you don't mind dropping support for iOS 3.0/3.1/3.2, you can use the UIWindow.rootViewController property:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {
UIViewController *vc = [[[QuizViewController alloc] init] autorelease];
window.rootViewController = vc;
[window makeKeyAndVisible];
return YES;
}
I am trying to make an app that contains a class that is created in the app delegate.
I initialize it with:
Mobile *tmp = [[Mobile alloc] init];
mobile = tmp;
[tmp release];
and then I try to use it in other classes in my app with this:
projectAppDelegate *delegate = (projectAppDelegate *)[[UIApplication sharedApplication] delegate];
mobile = delegate.mobile;
but when I do something like:
[mobile enter:x :y];
it crashes...
Is there something I did wrong, or is there any solution for making a class that all the other classes in the app can use?
Thank you.
If you want to use instances of your object you have to store them as properties of app delegate.
//appdelegate.h
//
//...
//
#interface AppDelegate : NSObject <UIApplicationDelegate> {
Mobile *tmp;
}
//...
//appdelegate.m
//
//...
//
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
mobile = [[Mobile alloc]init];
}
//...
- (void)dealloc {
[mobile release];
[super dealloc];
//...
}
Than you have to get a pointer to your application delegate shared instance and call your mobile property.
//... Somewhere
AppDelegate* ref = (AppDelegate*) [[UIApplication sharedApplication] delegate];
NSLog(#"%#", [ref mobile]);
//...
In your first code snippet you are effectively creating and immediately destroying the object. If the object is supposed to persist after that method is done executing you should just use
mobile = [[Mobile alloc] init];
I am just curious with regards to the correct way to create a view controller programatically. When I compile this code with the static analyser I get a leak (as you would expect) from the alloc. Should I just leave it as it needs to stay until the app exits anyways, or is there a cleaner way?
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(#"UIApplication application:");
RectViewController *myController = [[RectViewController alloc] init];
[window addSubview:[myController view]];
[window makeKeyAndVisible];
return YES;
}
cheers Gary
In this case, keep a reference to your view controller as an instance variable on the AppDelegate and release it in the AppDelegate's dealloc method.
#interface AppDelegate : NSObject {
// ...
RectViewController *myController;
}
// ...
#end
#implementation AppDelegate
// ...
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(#"UIApplication application:");
myController = [[RectViewController alloc] init];
[window addSubview:[myController view]];
[window makeKeyAndVisible];
return YES;
}
- (void) dealloc {
// ...
[myController release];
[super dealloc];
}
// ...
#end
Keep a reference to the view controller in you app delegate (instance, property, synthesize and release in dealloc).
And then instantiate it like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(#"UIApplication application:");
RectViewController *rootControllerTemp = [RectViewController new];
self.rootController = rootControllerTemp;
[rootControllerTemp release];
[window addSubview:[self.rootController view]];
[window makeKeyAndVisible];
return YES;
}