iOS Memory Management, UIWindow addSubview:, and tab controller - objective-c

When using the following code, do you need to do anything to release the tab controller's view? While working on an app, I've analyzed the project and it reports that there is a potential link on the line using addSubview:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:[tabController view]];
[window makeKeyAndVisible];
return YES;
}
MyAppDelegate.h
#interface MyAppDelegate : AppDelegate {
UIWindow *window;
IBOutlet UITabBarController *tabController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabController;
#end
MyAppDelegate.m
#implementation MyAppDelegate
#synthesize tabController, window;
- (void)dealloc {
[tabController release];
[window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:[tabController view]];
[window makeKeyAndVisible];
return YES;
}
#end
Do I need to release tabController.view before I release the tabController? Or inside the application:didFinishLaunchingWithOptions: method? This question seems to say that I just need to release the controller in dealloc.
Thanks!

Technically, you should release the tabController at some point, but in reality it does not matter. The window exists for the life of the program, and when the program is terminated all memory is purged, so there will be no leaks for objects retained by the window.
This will raise an alert when you run Build/Build and Analyze, but it can safely be ignored. The same applies to a navController added at this level or any other retained subview of the main window.

u have sythesized so call it with self
- (void)dealloc {
[self.tabController release];
self.tabController=nil;
[self.window release];
self.window = nil;
}

Related

Objective C - iOS - UIWebView getting retained in ARC but Delegate methods are not being called

I am having an issue with ARC. It is not retaining the webview. The scenario is I have to send a webview from one viewcontroller to another one. The reason is when the user searches for something I want to take him to a new screen with some other options. (I have to use the same webview)
Here is the sample code: I have a ViewController1 which has a webview (I added it in the xib.) I am loading say google in it and once the user searches for something and when its done loading I have to take him to a new view controller and show the same webview in the new viewcontroller.
//ViewController1
#interface ViewController1 : UIViewController <UIWebViewDelegate>
#property (nonatomic, retain) UIWebView* testWebView;
#end
#implementation ViewController1
#synthesize testWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
testWebView = [[UIWebView alloc]init];
testWebView.delegate = self;
[testWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"https://www.google.com"]]];
}
-(void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *html = [testWebView stringByEvaluatingJavaScriptFromString:
#"document.body.innerHTML"];
if ([self.testWebView.request.url.absoluteString rangeOfString:#"output=search"].location != NSNotFound) {
ViewController2* newViewController = [[ViewController2 alloc] init];
[newViewController setTestWebView:self.testWebView];
[self.navigationController setViewControllers:[NSArray arrayWithObject:newViewController] animated:NO];
}
}
- (void)dealloc{
[self.testWebView stopLoading];
self.testWebView.delegate = nil;
self.testWebView = nil;
}
In the second view controller I am loading stackoverflow.com after a delay of 10 secs. The problem is it is loading stackoverflow fine, but it is not calling any of the delegate methods. Why?
#interface ViewController2 : UIViewController <UIWebViewDelegate>
#property (nonatomic, retain) UIWebView* testWebView;
#end
#implementation ViewController2
#synthesize testWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.testWebView.delegate = self;
[self.view addSubview:testWebView];
[self performSelector:#selector(loadDifferentPage) withObject:nil afterDelay:10];
}
-(void)loadDifferentPage{
[self.testWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.stackoverflow.com/"]]];
}
-(void)webViewDidStartLoad:(UIWebView *)webView{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
-(void)webViewDidFinishLoad:(UIWebView *)webView{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(#"%s", __PRETTY_FUNCTION__);
return YES;
}
ViewController2 is retaining the webview but the delegate methods are not being called. Why?
Thanks
Sai
ViewController1 delloc method was causing the issue:
If I uncomment out self.textWebView.delegate = nil it works fine. The reason is first we are setting the webview for newViewController and later in dealloc of ViewController1 we are setting its delegate to nil.
- (void)dealloc{
[self.testWebView stopLoading];
if(self.testWebView.delegate == self)
self.testWebView.delegate = nil;
self.testWebView = nil;
}
First thing I noticed is you're not specifying the instance variable name when synthesizing a property. That's just asking for collisions. Here's an example of how that should look:
#interface ViewController1 : UIViewController <UIWebViewDelegate>
#property (nonatomic, strong) IBOutlet UIWebView* testWebView;
#end
#implementation ViewController1
#synthesize testWebView=_testWebView;
Also, I noticed in ViewController1 you used IBOutlet so everything is probably wired up in Interface Builder. Try making sure that you set the delegate property in Interface Bulider because you don't set it in the implementation. That would be why you're not receiving any messages.
ViewController2 looks like you set the delegate in code. The problem is, you DON'T have IBOutlet in front of the property. Normally this would mean that you simply setup the WebView in code, but in your example you do not ever create a new instance of a UIWebView control and assign it to self.testWebView. This means that if it does display on the page, it's because Interface Builder was used to create it. You couldn't set the delegate in code without using IBOutlet in front of the testWebView declaration so that's probably why it's not working in exmaple two.
#interface ViewController2 : UIViewController <UIWebViewDelegate>
#property (nonatomic, strong) UIWebView* testWebView; // Mising IBOutlet
#end
#implementation ViewController2
#synthesize testWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
// missing any code that would create the webview [[UIWebView alloc] init]
self.testWebView.delegate = self; // You set the delegate in code here
[self.view addSubview:testWebView];
[self performSelector:#selector(loadDifferentPage) withObject:nil afterDelay:10];
}
Hope this helps, I'd have to see your full implementation to get more specific than this.

How to create a global reference for iAd and implement in multiple Viewcontrollers

I'm having 5 ViewControllers in each of that i have to display iAd,so that i have to implement iAd code in each ViewControllers. Instead of that if i create set of common code in AppDelegate means i can just call that code wherever i need iAd to be displayed.
If anyone has implemented this iAd concept means help me to get out of this issue. Thanks in Advance.
Just create a Pointer to iAD in APP delegate.
.h:
#property (strong, nonatomic) ADBannerView *UIiAD;
.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UIiAD = [[ADBannerView alloc] init];
return YES;
}
Then in your ViewControllers do this:
.h:
#property (strong, nonatomic) ADBannerView *UIiAD;
.m:
- (AppDelegate *) appdelegate {
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (void) viewWillAppear:(BOOL)animated {
UIiAD = [[self appdelegate] UIiAD];
UIiAD.delegate=self;
// CODE to correct the layout
}
- (void) viewWillDisappear:(BOOL)animated{
UIiAD.delegate=nil;
UIiAD=nil;
[UIiAD removeFromSuperview];
}
Do this for all the view controllers with appropriate code to redesign the layout!
hi this looks like a good answer but don't you have to import the AppDelegate in your m files
#import "AppDelegate.h"
and shouldn't you hide the add if no network connection or add not showing
In the AppDelegate.h:
#import <iAd/iAd.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
ADBannerView *_bannerView;
...
...
}
#property (nonatomic, retain) ADBannerView *_bannerView;
In the AppDelegate.m:
#synthesize _bannerView;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
if ([ADBannerView instancesRespondToSelector:#selector(initWithAdType:)]) {
self._bannerView = [[ADBannerView alloc] initWithAdType:ADAdTypeBanner];
} else {
self._bannerView = [[ADBannerView alloc] init];
}
...
}
In the view controllers that you need to add iAd, create a container UIView with name 'vwAd' and make the connection in xib file where you want to display iAds.
#interface ViewController : UIViewController<ADBannerViewDelegate>
{
IBOutlet UIView *vwAd;
...
...
}
- (void)layoutAnimated:(BOOL)animated
{
CGRect contentFrame = self.view.bounds;
if (contentFrame.size.width < contentFrame.size.height) {
self.appDelegate._bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
} else {
self.appDelegate._bannerView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
}
CGRect bannerFrame = self.appDelegate._bannerView.frame;
if (self.appDelegate._bannerView.bannerLoaded) {
bannerFrame.origin.y = 0;
} else {
bannerFrame.origin.y = vwAd.frame.size.height;
}
[UIView animateWithDuration:animated ? 0.25 : 0.0 animations:^{
self.appDelegate._bannerView.frame = bannerFrame;
}];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
...
[self.appDelegate._bannerView removeFromSuperview];
self.appDelegate._bannerView.delegate = nil;
self.appDelegate._bannerView.delegate = self;
[vwAd addSubview:self.appDelegate._bannerView];
[self layoutAnimated:NO];
}
Please also review iAdSuite examples from Apple. Original layoutAnimated function can be found in iAdSuite examples. Do not forget to add the delegate functions from iAdSuite example into your view controller.

Applications are expected to have a root view controller at the end of application launch wait_fences: failed to receive reply: 10004003

when i launch my application in iphone emulator, it goes without any problem. If i launch on device it will crash as still as i open it. But when i launch emulator it says that problem:
Applications are expected to have a root view controller at the end of application launch
wait_fences: failed to receive reply: 10004003
What can i do?
This is my application delegate:
.h :
#import <UIKit/UIKit.h>
#class TrovaChiaviViewController;
#interface TrovaChiaviAppDelegate : NSObject <UIApplicationDelegate>
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet TrovaChiaviViewController *viewController;
#end
and this is .m
#import "TrovaChiaviAppDelegate.h"
#import "TrovaChiaviViewController.h"
#implementation TrovaChiaviAppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[application release];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
#end
I don't see where you are setting your viewController property, add
self.viewController = [[[TrovaChiaviViewController alloc] initWithNibName:#"TrovaChiaviViewController" bundle:nil] autorelease];
before
self.window.rootViewController = self.viewController;
I just had this issue when I was changing the application from iPhone only to iPad. The error message "Applications are expected to have a root view controller at the end of application launch" was happening because in the application-info plist, there was an entry specifying a nib file for MainWindow when I was no longer using a nib.
Although this is an older thread, I thought I'd contribute. Sometimes I select a template when creating new projects. There were two cases when I created empty projects. In both of these cases, I encountered the same problem.
Refer to this link.
http://www.mumuen.com/2011/09/applications-are-expected-to-have-root.html
In my case, the key was that I had failed to allocate space for both my window and root controller.

Programmatically changing a UILabel from the App Controller in a Navigation Based iOS App

I'm having a lot of trouble with what seems like a very simple thing. I cannot update a UILabel programmatically from a Navigation-based iOS App. I don't want to use a button as this label is designed to report the status of an external system, and should update on launch. There is no need to make the user go though the extra step on touching the button if I don't have to.
The following is a somewhat exhaustive list of the steps I've taken. I'm sorry if some of this seems unnecessary, but in my experience even the smallest forgotten step can be the cause of the issue.
From a fresh Navigation-based App in Xcode here are the steps I'm taking:
Replace UITableView with a generic UIView class
Re-wire File's Owner's view outlet to the new UIView
Add a UILabel to the center of the UIView, make the text centered, and leave the default text.
Save and Exit Interface Builder
RootViewController.h
#import <UIKit>
#interface RootViewController : UIViewController {
UILabel *myLabel;
}
#property (nonatomic, retain) IBOutlet UILabel *myLabel;
#end
RootViewController.m
#import "RootViewController.h"
#implementation RootViewController
#synthesize myLabel;
...
Removed TableView stuff from RootViewController.m
Wire IBOutlet myLabel to the Label in RootViewController.xib
Save and Exit Interface Builder
tempNavAppAppDelegate.m
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
RootViewController *rootViewCont = navigationController.visibleViewController;
rootViewCont.myLabel.text = #"test";
NSLog(#"Label Text: %#", rootViewCont.myLabel.text);
return YES;
}
...
Build/Run
The Label shows as "Label" not "test". And the log reports:tempNavApp[94186:207] Label Text: (null)
I've tried a number of different ways to get this done, but any help would be appreciated.
The Journey
After discovering that my rootViewCont.myLabel was also nil, thanks to the help of mprudhom, I decided to test and see if I could assign myLabel.text a value in RootViewController.m's - (void)viewDidLoad method.
It worked, I was able to change the text directly from the RootViewController. But while this proved my View Controller wasn't broken, it did not solve my initial desire to change the UILabel from tempNavAppAppDelegate.m.
Elliot H. then suggested that navigationController.visibleViewController wasn't actually returning a view controller. I had tested for the value of rootViewCont and it came back as a RootViewController, but Elliot's suggestion got me thinking about the app's lifecycle and when the different parts of my code was actually loaded up.
So I started printing an NSLog at each step of the launch process (application:didFinishLaunchingWithOptions:, applicationDidBecomeActive:, viewDidLoad, viewDidAppear:), and discovered to my surprise that [self.window makeKeyAndVisible]; does not mean that the view will load before application:didFinishLaunchingWithOptions: is complete.
With that knowledge in hand I knew where the problem was. The solution (or at least my solution) seems to be NSNotificationCenter. I have now registered for notifications in tempNavAppAppDelegate and I am broadcasting a notification in RootViewController's viewDidAppear: method.
The Pertinent Code
RootViewController.h:
#interface RootViewController : UIViewController {
IBOutlet UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
RootViewController.m:
#implementation RootViewController
#synthesize myLabel;
- (void)viewDidLoad {
[super viewDidLoad];
NSParameterAssert(self.myLabel);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:#"viewDidAppear" object:self];
}
tempNavAppAppDelegate.h:
#interface tempNavAppAppDelegate : NSObject {
UIWindow *window;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
- (void)viewDidAppearNotification:(id)notification;
#end
tempNavAppAppDelegate.m:
#implementation tempNavAppAppDelegate
#synthesize window;
#synthesize navigationController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(viewDidAppearNotification:) name:#"viewDidAppear" object:nil];
return YES;
}
- (void)viewDidAppearNotification:(id)notification
{
NSString *noteClass = [NSString stringWithFormat:#"%#", [[notification object] class]];
if ([noteClass isEqualToString:#"RootViewController"]) {
RootViewController *noteObject = [notification object];
noteObject.myLabel.text = #"Success!";
}
}
If this code is printing nil:
rootViewCont.myLabel.text = #"test";
NSLog(#"Label Text: %#", rootViewCont.myLabel.text);
Then almost certainly it is because rootViewCont.myLabel itself is nil. Try logging the value of rootViewCont.myLabel as well and you'll see.
Are you sure you wired up the label to your UILabel IBOutput declaration in Interface Builder? That's most commonly the problem.
I personally always assert all my expected outlets in viewDidLoad so that I catch early on when the outlets have been (accidentally or not) been decoupled in Interface Builder. E.g.:
- (void)viewDidLoad {
[super viewDidLoad];
NSParameterAssert(rootViewCont.myLabel);
}
your interface should look like this
#import <UIKit>
#interface RootViewController : UIViewController {
// IBOutlet here...
IBOutlet UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
Is visibleViewController actually returning the view controller? My guess is since application:didFinishLaunchingWithOptions: hasn't returned yet, it's possible UINavigationController hasn't properly configured that property to return yet, even though you've added the navigation controller's subview to the view hierarchy, it's probably that visibleViewController isn't valid until after viewDidAppear: is called on the view controller in question.
Try having an IBOutlet to the RootViewController directly, or create it programmatically, and then assign the label text.
Just a general reminder: If an object is nil (in this case visibleViewController would be returning nil), and you send it a message, you won't crash, because messages to nil are valid and won't do anything. When you call the myLabel accessor on the rootViewCont object, if rootViewCont is nil, myLabel will return nil always.

When my UIViewController accesses an NSArray in my AppDelegate from an IBAction it crashes the program

I have a couple UIViewControllers that I am trying to access an array inside my AppDelegate. When I use an IBAction UIButton and in that method I access my AppDelegate my program dies silently. Nothing in output or the debugger, it just stops. If I run it several times I can see that it is failing to access the array properly.
To investigate this problem I created a very basic app.
In my AppDelegate.h I declared and set properties for the array
#import <UIKit/UIKit.h>
#class MyViewController;
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MyViewController *viewController;
NSArray *images;
}
#property (nonatomic, retain) NSArray *images;
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet MyViewController *viewController;`
In the AppDelegate.m I synthesised and initialized the NSArray (Also made sure the images were added to the Resources folder).
#synthesize images;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
images = [NSArray arrayWithObjects:
[[NSBundle mainBundle] pathForResource:#"bamboo_nw" ofType:#"jpg"],
.....
nil];
NSLog(#"init images size:%i",[images count]);
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
In my UIViewController.h I added class, imported header file, declared, and set properties for my AppDelegate pointer.
#import <UIKit/UIKit.h>
#import "MyAppDelegate.h"
#class MyAppDelegate;
#interface MyViewController : UIViewController {
MyAppDelegate *mainDelegate;
IBOutlet UIButton mybutton;
}
#property (nonatomic, retain) MyAppDelegate mainDelegate;
#property (nonatomic, retain) UIButton *mybutton;
-(IBAction) doSomething;`
In my UIViewController.m I synthesize and assign my AppDelegate. I set up an IBAction that will log the same count of the NSArray from the AppDelegate.
#import "MyViewController.h"
#import "MyAppDelegate.h"
#implementation MyViewController
#synthesize mybutton;
- (void)viewDidLoad {
mainDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"vdl images size:%i",[mainDelegate.images count]);
[super viewDidLoad];
}
-(IBAction) doSomething {
NSLog(#"ds images size:%i",[mainDelegate.images count]);
}
I print the size of the NSArray in the AppDelegate when I create it, in the ViewController when I first assign my AppDelegate pointer, and then as a result of my IBAction.
I find that everytime I hit the button the program dies. On the third time I hit the button, I saw that it ran my IBAction but printed my array size as 1 instead of 8. Am I missing something? Also, why don't I get stack traces or anything, the debugger just dies silently?
Thanks in advance for any help!
Debugger Console output for 3 runs:
[Session started at 2010-05-10 06:21:32 -0700.]
2010-05-10 06:21:44.865 My[59695:207] init images size:8
2010-05-10 06:21:47.246 My[59695:207] vdl images size:8
[Session started at 2010-05-10 06:22:15 -0700.]
2010-05-10 06:22:18.920 My[59704:207] init images size:8
2010-05-10 06:22:19.043 My[59704:207] vdl images size:8
[Session started at 2010-05-10 06:22:23 -0700.]
2010-05-10 06:22:25.966 My[59707:207] init images size:8
2010-05-10 06:22:26.017 My[59707:207] vdl images size:8
2010-05-10 06:22:27.814 My[59707:207] ds images size:1
You need to use self.images as your lvalue in application:DidFinishLaunchingWithOptions:, to wit:
#synthesize images;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions(NSDictionary *)launchOptions {
self.images = [NSArray arrayWithObjects:
[[NSBundle mainBundle] pathForResource:#"bamboo_nw" ofType:#"jpg"],
.....
nil];
NSLog(#"init images size:%i",[images count]);
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
When you use the ivar images without the self, you are giving up the goodness of the automatic retention that you set up when you created the #property.