iOS App leaking - iphone

I am having a Navigation based app with a few buttons on the first View (not using ARC). By touching one button optionPressed gets triggered to push to another View.
When I analyse the code for leaks. I get the following warning. "Potential leak of an object" [self.displayViewController setCurrentPhoto:sender.currentTitle];
How should I release the self.displayViewController and where if that's the cause.
.h
#import <UIKit/UIKit.h>
#import "DisplayViewController.h"
#class DisplayViewController;
#interface Pocket_DjangoViewController : UIViewController
- (IBAction)optionPressed:(UIButton *)sender;
#property (retain, nonatomic) DisplayViewController *displayViewController;
#end
.m
- (IBAction)optionPressed:(UIButton *)sender
{
if (!self.displayViewController) {
self.displayViewController = [[DisplayViewController alloc] initWithNibName:#"DisplayViewController" bundle:nil];
}
[self.displayViewController setCurrentPhoto:sender.currentTitle];
[self.navigationController pushViewController:self.displayViewController animated:YES];
//[self.displayViewController release];
//self.displayViewController = nil;
}

The leak stems for this line:
self.displayViewController = [[DisplayViewController alloc] initWithNibName:#"DisplayViewController" bundle:nil];
you should have:
self.displayViewController = [[[DisplayViewController alloc] initWithNibName:#"DisplayViewController" bundle:nil] autorelease];
In your actual code, you are creating an object:
[[DisplayViewController alloc] initWithNibName:#"DisplayViewController" bundle:nil];
which is already retained; then you assign it to a retain property:
#property (retain, nonatomic) DisplayViewController *displayViewController;
and this will create a retain unbalance, as the original alloc is never released and only the retain called by the property is eventually released.

Related

Adding a TableViewController to an existing project

I have an existing TableViewController as follows
// TableViewController.h
#interface TableViewController : UITableViewController { NSArray *dataArray; }
#property (nonatomic, retain) NSArray *dataArray;
And a navAppDelegate - to be specific:
// navAppDelegate.h
#import <UIKit/UIKit.h>
#interface navwAppDelegate : NSObject
<UIApplicationDelegate, UINavigationControllerDelegate> {
UIWindow *window;
IBOutlet UINavigationController *navigationController;}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) UINavigationController *navigationController;
// navAppDelegate.m
#import "navAppDelegate.h"
#implementation navigationtableviewAppDelegate
#synthesize window, navigationController;
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
[window makeKeyAndVisible];
[window addSubview:[navigationController view]];
}
Now, I simply added the files to an existing project, except I put the content of (void)applicationDidFinishLaunching{} in a (void)viewDidLoad{} since it's a view from now on and not a window (right?). It doesn't work and my guess is that I have to change all the window calls from above to a view? What am I making here fundamentally wrong?
I'm making a call from
// StartOffHere.m
- (void)LetsGoButtonTouched {
navAppDelegate *newview = [[navAppDelegate alloc] initWithNibName:nil bundle:nil]; // I get a SIGABRT here
[[self navigationController] pushViewController: newview animated: YES];
}
- (void)LetsGoButtonTouched {
TableViewController *tableViewController = [[TableViewController alloc] init];
[[self navigationController] pushViewController:tableViewController animated: YES];
}
Try that. Is that what you wanted to happen?
If so, what I have done is created a new instance of your table view controller and pushed that. In your original code, you were trying to push on the app delegate which cannot be done; the app delegate is not a view controller. TableViewController, your subclass of UITableViewController, is though, so you can use this for the pushViewController: method - and the table view will appear on screen.
Thanks Benjamin. Excellent. But it didn't work quite that way though - after two hard days I managed it to run. For those who are interesed, here's what I did:
If you want to add an existing TableViewController with a NavigationController you'll only need the TableViewController and the DetailViewController files. Forget the AppDelegate.
Do twice new file -> Subclass and copy your existing code into identical TableViewController .h/.m/.xib - and DetailViewController .h/.m/.xib respectively.
When you make the method call you have to integrate both the TableViewController and the NavigationController - like this:
- (void)LetsGoButtonTouched {
TableViewController *tvc = [[[TableViewController alloc] init] autorelease];
UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:tvc]autorelease];
[self presentModalViewController:navigationController animated:YES];
By the way, this question gave me the hint:
Add a UINavigationBar to a UITableViewController without a UINavigationController

Objective-c Novice - Needs help with UIViews

Im new to iphone development and after lots of reading on it im still trying to figure out how UIViews operate properly. I have been playing about with it and i this is where i am at so far:
I have created a new xcode project using the view-based application. I have my MMAppViewController classes and i created a new UIViewController subclass called "Level1View".
There is a button titled "Level 1" that takes me to the "Level1View" viewController. In this viewController there is there is a "next" button, a "main menu" button (that returns to MMAppViewController) and there is a label, currently titled "Level 1".
My problem is that the code i have used to change the title of label does not work! Does anyone know why this is? Here is my code:
#class MMAppViewController;
#interface MMAppAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MMAppViewController *viewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet MMAppViewController *viewController;
#end
and
#implementation MMAppViewController
-(IBAction)pushLevel1{
Level1View *level1View = [[Level1View alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:level1View animated:YES];
}
...
and
#import <UIKit/UIKit.h>
#interface Level1View : UIViewController {
IBOutlet UILabel *labelTitle;
}
-(IBAction)pushBack;
-(IBAction)pushNext;
#end
and
#import "Level1View.h"
#import "MMAppViewController.h"
#implementation Level1View
-(IBAction)pushBack{
MMAppViewController *MainView = [[MMAppViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:MainView animated:YES];
}
-(IBAction)pushNext{
[labelTitle setText:(#"Thanks for playing :)")];
}
- (void)didReceiveMemoryWarning {
...
Currently the app runs but the label wont change when i hit the "next" button. Can anyone help?
are you sure a UINavigationController isn't a better tool for the job you want to do? That will make it easy for you to manage a stack of UIView objects.
That said, have you tried adding logging to make sure your pushNext method is getting called? where is labelTitle declared? Did you use a XIB or not?
Did you bind the Label in Interface Builder to the labelTitle outlet in your Level1View?
If you forget that step, the outlets won't work. Even after several years, I still forget this step sometimes.
--Mike
are you sure you connected the label in IB?
and if you set a property "#property (nonatomic, retain) IBOutlet UILabel *labelTitle;" in Level1View.h you can access it from Main View:
Level1View *level1View = [[Level1View alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:level1View animated:YES];
level1View.labelTitle.text = #"something";
[level1View release];
The other thing you shouldn't present the Main View Controller again instead dismiss the Level1View with:
#implementation Level1View
-(IBAction)pushBack{
[self dismissModalViewControllerAnimated:YES];
}
//
and maybe the problem is [[Level1View alloc] initWithNibName:nil bundle:nil] you have to specify the nib you want to load e.g. [[Level1View alloc] initWithNibName:#"Level1View" bundle:nil]
Declare labelTitle as a property in your header file, and synthesize labelTitle in your .m - as long as labelTitle is hooked up through interface builder the rest of your code is fine.
.h
#property (nonatomic, retain) IBOutlet UILabel *labelTitle;
.m
#synthesize labelTitle;
Then your setter call will work. (also, dot-notation works for synthesized properties so you may as well use it)
change
[labelTitle setText:(#"Thanks for playing :)")];
to
labelTitle.text = #"Thanks for playing :)";
Synthesizing a property will create setter and getter methods at runtime. Read: The Objective-C Programming Language

How can I update my view controller's UILabel through another class?

I'm my class i've added a an instance of my view controller, created a property and then synthesized it in my implementation file. I am trying to update the UIlabel in the view controller like this,
NSString *currentChar = [[NSString alloc] initWithFormat:#"%c", ch];
viewController.outputLabel.text = currentChar;
[currentChar release];
My problem is that everything builds without any errors or warnings but the label just doesn't get updated, what am I doing wrong. I'd really appreciate some help on this one.
Are you sure you're referencing the existing viewController and you didn't instantiate a new one? Your property is not declared as copy, correct?
textProcessor.h / .m
#interface textProcessor : NSObject {
MainViewController *mainView;
}
#property (retain) MainViewController *mainView;
#end
#implementation textProcessor;
#synthesize mainView;
MainViewController.h / .m
#interface MainViewController : UIViewController {
UILabel *myLabel;
}
#property (retain) UILabel myLabel;
#end
#implementation MainViewController
#synthesize myLabel;
When you are initializing your textProcessor class, and you set the value for "mainView" like
-(void)viewDidLoad {
[super viewDidLoad];
textProcessor *proc = [[textProcessor alloc] init];
proc.mainView = self;
//note that you are not doing this:
//MainViewController *mainView = [[MainViewController alloc] init];
//proc.mainView = mainView;
//that was creating a new instance variable instead of using self, the existing one
[textProcessor release];
}
Have you created your label in IB? If you are using IB you have to create an IBOutlet for your UILabel. You then make a connection between the UILabel in IB to your IBOutlet in your class.
Have you tried calling the setNeedsDisplay method on the view? Also you may want to try using the setText method instead of assigning directly to the property.

Unused IBOutlets leaking

So, I'm loading by XIB file and it contains a set of UIBarButtonItems. Some of the items are used when the viewDidLoad: is called.
#interface MyViewController : UIViewController {
IBOutlet UIBarButtonItem *addButton;
IBOutlet UIBarButtonItem *editButton;
IBOutlet UIBarButtonItem *doneButton;
}
// NB: There are no properties retaining anything.
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *initialToolbarItems =
[[NSArray alloc] initWithObjects: addButton, editButton, nil];
self.toolbarItems = initialToolbarItems;
[initialToolbarItems release];
}
- (void)dealloc {
[super dealloc];
// Nothing else to do here since we are not retaining anything.
// … or are we? <insert dramatic music here>
}
#end
If I push the above the above ViewController onto a UINavigationController everything seems fine, all the IBOutlets are assigned and behave like expected.
The instant i pop the ViewController from the navigation stack Instruments' Leaks tells me that I am leaking a UIBarButtonItem. Woe is me!
If I change dealloc: to
- (void)dealloc {
[doneButton release];
[super dealloc];
}
no leaks occur. The same goes if I use doneButton in viewDidLoad:
NSArray *initialToolbarItems =
[[NSArray alloc] initWithObjects: addButton, editButton, doneButton, nil];
My question: Why is my IBOutlet leaking when I don't use it. I don't retain it at any point. The the NIB loader should own the object, right?
Only thing I can think of:
The nib loader treats IBOutlets as strong references. All outlets are retained by default unless you specifically indicate assign. So you still need to release them in dealloc and viewDidUnload.
You can also use a assigned property to make it a weak reference:
#property (nonatomic, assign) IBOutlet UIBarButtonItem *doneButton;
Some reading: http://weblog.bignerdranch.com/?p=95
If you have #property with (retain) declared for the your IBOOutlets they will be retained and must be released
The array retains them

Is that right if I switch View in this way... (IPhone)

I have a MyAppAppDelegate, it contains a window, and a UITabBarController.
#interface MyAppAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
IBOutlet UITabBarController *rootController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *rootController;
#end
And I have View A, that contain a button to switch to View B. It is the .h file:
#import <UIKit/UIKit.h>
#class MyAppAppDelegate;
#class ViewBController;
#interface ViewAController : UIViewController {
IBOutlet UIView *view;
IBOutlet UIButton *switchToViewBButton;
}
#property (retain, nonatomic) UIView *view;
#property (retain, nonatomic) UIButton *switchToViewBButton;
-(IBAction) startSwitching: (id)sender;
#end
And it is the.m file:
#import "ViewAController.h"
#import "ViewBController.h"
#import "MyAppAppDelegate.h"
#implementation ViewAController
/*skip the default generated codes*/
-(IBAction) startClock: (id)sender{
NSLog(#"Start Switching");
[rootController presentModalViewController:ViewBController animated:YES];
}
Plz notice that the ViewB is not enable to display on UITabBarController, it only appear, when the ViewA button is clicked. Also, I found that the debugger tell me that the rootController is undeclared. but I already import MyAppDelegate to the file. thz a lot... ...
You need to synthesize the rootController instance:
#synthesize rootController;
Then it should work. Put this line of code below the implementation line in the .m file. There is no reason why you should be getting the second error, so try my solution and then tell us what happened.
Also, please try to write in complete sentences. In my experience, if you write well in a forum post, you will gain more respect from people who might help you.
No you need to do something like this:
ViewBController* vc = [[ViewBController alloc] initWithNib: #"ViewBController" mainBundle: nil];
if (vc != nil) {
[rootController presentModalViewController: vc animated:YES];
[vc release];
}
The mistake that you are making is that you are passing presentModalViewController: the class of the ViewBController. Instead it needs an instance.
ViewBController* viewBController = [[[ViewBController alloc] initWithNibName: #"NameOfViewBControllerNibFile" bundle:nil] autorelease];
[self presentModalViewController:viewBController animated:YES];
You can not access rootController from ViewAController, because it is a property of MyAppAppDelegate, not ViewAController. If you want to access the UITabBarController in charge of ViewAController, then inside ViewAController you use self.tabBarController
So if you want the UITabBarController to do the above, change it to
ViewBController* viewBController = [[[ViewBController alloc] initWithNib: #"NameOfViewBControllerNibFile" mainBundle: nil] autorelease];
[self.tabBarController presentModalViewController:viewBController animated:YES];
ViewBController *vc = [[[ViewBController alloc] initWithNib:#"ViewBController"
mainBundle:nil] autorelease];
MyAppDelegate *appDelegate = (MyAppAppDelegate *)[UIApplication sharedApplication].delegate;
[appDelegate.rootController presentModalViewController:vc animated:YES];