Where's the memory leak? - objective-c

I'm adding a custom view to a tableHeaderView with the following code:
imageButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
imageButton.frame = CGRectMake(120, 12, 64, 64);
imageButton.titleLabel.font = [UIFont systemFontOfSize:10];
imageButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
imageButton.titleLabel.textAlignment = UITextAlignmentCenter;
[imageButton setTitle:NSLocalizedString(#"Choose\nPhoto", #"Choose\nPhoto") forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(photoButtonPressed) forControlEvents:UIControlEventTouchUpInside];
// add existing image, if any, to button
if (child.thumbnailImage != nil) {
[imageButton setBackgroundImage:child.thumbnailImage forState:UIControlStateNormal];
}
// add button to view
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(22, 12, 70, 70)];
[headerView addSubview:imageButton];
// add view to table header
self.tableView.tableHeaderView = headerView;
The memory leak is showing on the alloc line above for the headerView UIView.
I'm declaring the UIView and the UIButton in the header file and releasing it in ViewDidUnload and dealloc, so I'm not sure what I'm doing wrong.
Also, this is only showing up on the device and not the simulator (just thought I'd mention).
Any help would be appreciated.
Thanks,
Rod

You're using the headerView setter method (self.headerView) so you need to release the UIView instance you assign to the headerView property, either using release or autorelease.
e.g.
UIView* newHeaderView = [[UIView alloc] initWithFrame...];
self.headerView = newHeaderView;
[newHeaderView release];
or
self.headerView = [[[UIView alloc] initWithFrame:...] autorelease];
The reason is because the headerView setter method automatically retains the object assigned to it.
Alternatively, you can set the headerView instance variable directly, without using the property setter method:
[headerView release]; // release any existing object
headerView = [[UIView alloc] initWithFrame:...];

Related

Can't see the button

- (void)viewDidLoad{
int leftBorder = 80;
int topBorder = 160;
int width = 150;
int height = 50;
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(leftBorder, topBorder, width, height)];
myView.layer.cornerRadius = 5;
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
UIButton *testButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
testButton.frame = CGRectMake(0, 0, 50, 50);
[testButton setTitle:#"testButton" forState:UIControlStateNormal];
[testButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.myView addSubview:self.testButton];
self.myView.hidden = YES;
[super viewDidLoad];
}
Hi, sorry for stupid question! I'm newby in xcode. Why I didn't see this button? And How can I hide button after click? I need button inside the frame.
Simple remove self.myView.hidden = YES;
To add you click listener, two solution:
By code in your viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
[mybutton addTarget:self action:#selector(myButtonClick:) forControlEvents:(UIControlEvents)UIControlEventTouchDown];
}
- (void)myButtonClick:(id)sender {
myButton.hidden = YES;
}
Or via interface Builder (preferred), The easiest way is to actually define the handler/action in Xcode using the IBAction declaration in the interface file (add the declaration just before the #end statement). Then attach the action to the button
you are adding self.testButton instead of the created testButton.
[self.myView addSubview:testButton];
you did not assign myView to your property.
[self.view addSubview:myView]; self.myView = myView;
remove self.myView.hidden = YES;
Another remark:
You should usually call super as early as possible. Otherwise, the superclass may interfere with your own implementation.
There are several issues in your code.
You add testButton as subview to self.myView. Then you hide self.myView. As a result neither self.myview nor its subviews will be visible.
You get confused with your local varialbles and instance varialbes. Apparently there is an instance variable and property myView. Otherwise you coudn't use self.myView. And you declare a local variable myView, which is not the same as the instance variable. This may well be perfectly ok. But I got a guts feeling that you did this not intentionally. Your self.myView may well be nil at the point in time where you add the subview. And even the subview self.testButton may be nil. That will compile file and execute fine but nothing does happen actually.
The same for testButton.
I suggest to change the code a bit, assuming there are properties for myView and testButton of the appropriate types:
self.myView = [[UIView alloc] initWithFrame:CGRectMake(leftBorder, topBorder, width, height)];
self.myView.layer.cornerRadius = 5;
self.myView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.myView];
self.testButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.testButton.frame = CGRectMake(0, 0, 50, 50);
[self.testButton setTitle:#"testButton" forState:UIControlStateNormal];
[self.testButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.myView addSubview:self.testButton];
self.myView.hidden = NO;

my UITableViewCell setting button

I'm having trouble adding button to my UITableViewCell, the cell has two UILabels and two UIImageViews, sometimes the UIImageView will contain an image and sometimes a button:
In my UITableViewCell subclass I have:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if ( self ) {
// Initialization code
firstLock = [[UILabel alloc]init];
[firstLock setBackgroundColor:[UIColor clearColor]];
firstLock.textAlignment = UITextAlignmentLeft;
firstLock.font = [UIFont fontWithName:#"Arial-BoldMT" size:17];
secondLock= [[UILabel alloc]init];
[secondLock setBackgroundColor:[UIColor clearColor]];
secondLock.textAlignment = UITextAlignmentRight;
secondLock.font = [UIFont fontWithName:#"Arial-BoldMT" size:17];
firstLockImage = [[UIImageView alloc]init];
secondLockImage = [[UIImageView alloc] init];
[self.contentView addSubview:firstLock];
[self.contentView addSubview:secondLock];
[self.contentView addSubview:firstLockImage];
[self.contentView addSubview:secondLockImage];
}
return self;
}
When one of the UIImageViews is just an image no problem, but it crashes when I add a UIButton (imaged) as a subview.
In the UITableViewDataSource implementation:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIImage *btnImage = [UIImage imageNamed:#"bike_ok.png"];
UIButton *button =[UIButton alloc];
[button setImage:btnImage forState:UIControlStateNormal];
[cell.secondLockImage addSubview:button];
Adding a button as a subview of the image view crashes:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Requesting the window of a view (<UIButton: 0x7bac7d0; frame = (0 0; 0 0); transform = [0, 0, 0, 0, 0, 0]; alpha = 0; opaque = NO; userInteractionEnabled = NO; layer = (null)>) with a nil layer. This view probably hasn't received initWithFrame: or initWithCoder:.'
*** First throw call stack:
What am I missing?
Thanks!
Just add! its important this line
[firstLockImage setUserInteractionEnabled:YES];
[secondLockImage setUserInteractionEnabled:YES];
Because the UIImageView has NO as default and the button doesn't work without it!
If you read the crash error it is quite easy to see where your issue is. You have not initialized your button.
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0,0,0,0)];
or you can use initWithCoder.
Hope this helps
Sam
Read the exception text - it says:
This view probably hasn't received initWithFrame: or initWithCoder:
Just a few lines up in your question, you are sending messages to a UIButton instance that you have only alloc'd and not sent any init... message to. That is your error.
Furthermore, you shouldn't directly call the alloc/init pair on UIButton as it's a class cluster and you should usually use +[UIButton buttonWithType:] to get a button instance.
EDIT I'm not 100% sure about that, actually. But you don't really know exactly what you'll get if you do initWithFrame: so I'd still go with buttonWithType: to get a custom button, which is what you want. END EDIT
So, change that line to:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
Hope this helps!
change UIButton *button =[UIButton alloc]; (alloc w/o init?) to
UIButton *button =[UIButton buttonWithType:UIButtonTypeCustom];
//set frame
[button setFrame:(CGRect){x,y,w,h}];
+buttonWithType handles the alloc/init for your UIButton object

Method returns an Objective-C object with a +1 retain count

I'm gettin' these while i analyze my code:
Method returns an Objective-C object with a +1 retain count
and
Object leaked: object allocated and stored into 'headerLabel' is not referenced later in this execution path and has a retain count of +1
on this method:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
// create the parent view that will hold header Label
UIView* customView = [[UIView alloc] initWithFrame:CGRectMake(15.0, 0.0, 300.0, 44.0)];
// create the button object
UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.textColor = [UIColor whiteColor];
headerLabel.highlightedTextColor = [UIColor whiteColor];
headerLabel.font = [UIFont boldSystemFontOfSize:15];
headerLabel.frame = CGRectMake(10.0, 0.0, 300.0, 44.0);
if (section == 0)
headerLabel.text = NSLocalizedString(#"A", #"A");
else if (section == 1)
headerLabel.text =NSLocalizedString(#"B", #"B");
else if (section == 2)
headerLabel.text = NSLocalizedString(#"C", #"C");
if(searching)
headerLabel.text = NSLocalizedString(#"SEARCH", #"Search Results");
[customView addSubview:headerLabel];
return customView;
}
Now, expanding the arrows i'm trying to understand, and i suppose that customView is not being deallocated. Is it right?
How can i do to fix it? I'm new to this, help me to understand!
Either add
[headerLabel release];
after
[customView addSubview:headerLabel];
or initialize it like this
UILabel * headerLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
of course given that you're not using ARC
[customView addSubview:headerLabel];
after this line you should release headerLabel variable.
It's important to understand the concept of object ownership. In
Objective C, an object owner is someone (or piece of code) that has
explicitly said "Right, I need this object, don't delete it". This
could be the person (or code) that created the object
. Or it could be another person (or code) that
received the object and needs it .
Thus an object can have more than one owner. The number of owners an
object has, is also the reference count.
Take a look at this Memory Management with Objective C / Cocoa / iPhone.
In your code , you created headerLabel , so you are the owner of that object; you must release that object.
headerLabel should be released and also if your method creates an instance and retains it, it's name has to start with "new", "copy" or "alloc"

How to perform a [self.view addSubview: lbl] outside of ViewController Class scope?

How to perform a [self.view addSubview: lbl] outside of ViewController Class scope ?
or:
How do I add a label or another view in the mainview, outside of the ViewController class, in a different class?
thanks
- (void)viewDidLoad {
[super viewDidLoad];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[lbl setText:#"hi there"];
[self.view addSubview:lbl];// <-- this works, but ...
// what is "self" referring to?
// and how can I declare and call from another class?
...
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[lbl setText:#"hi there"];
calcRomanAppDelegate *v = [[calcRomanAppDelegate new] init];
[v.viewController.view addSubview:lbl]; // this compiles, but...
// fails to shows a label on the form
...
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[lbl setText:#"hi there"];
calcRomanViewController *v = [[calcRomanViewController new] init];
[v.view addSubview:lbl]; // this just makes a call back to viewDidLoad... endless loop
}
Well, view is just a property of the UIViewController class. Assuming that you have your UIViewController *controller variable somewhere, you can just use
[controller.view addSubview:subview]
The reason that [v.viewController.view addSubview:lbl]; doesn't work is that v is a new instance of calcRomanAppDelegate. Every application has a shared instance of the app delegate, that can be accessed via [[NSApplication sharedApplication] delegate]. Therefore, your code would become:
calcRomanAppDelegate *v = (calcRomanAppDelegate *)[[NSApplication sharedApplication] delegate];
[v.viewController.view addSubview:lbl]; // this compiles but shows a blank form
Also In the code that you wrote, I will point out that the new method returns an initialized object, so you do not need the extra call to init in [[calcRomanAppDelegate new] init]. Instead of using the new method, I suggest using alloc, which doesn't call the initializer. Obviously that is not the issue in this particular case, but it's an important thing to know.
Not sure what you tried to accomplish.
But let's say you are in the view1 and want to create another view (view2) with your UILabel lbl added to this view2. Here are what you would do:
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(x, y, w, h)]; //x,y, w h are for your view2
[view addSubview:lbl];
[self.view addSubview:view2]; //self is your current viewcontroller - you add view2 on top of view1
On the other hand if you already have a ViewController class ViewController2.h, ViewController2.m and a ViewController2.xib defined. Here are what you would do:
ViewController2 *viewController2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
[viewController2.view addSubview:lbl];
[self.view addSubview:viewController2.view]; //same as before, you need to add viewController2's view to the current view
Hope this help.

Navigation Control Problem

I am using some methods to load a new .xib and go back to the main menu. However after about five time it crashes by using too much memory. I need to be able to go back to the main menu and to the game many times. Any other methods I should use for the navigation controls.
Main Menu part:
GameViewController* game = [[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil];
[self.navigationController pushViewController:game animated:NO];
Game part to return to main menu:
[self.navigationController popViewControllerAnimated:NO];
Here is the viewdidLoad
{
- (void)viewDidLoad {
[super viewDidLoad];
[self StartTimer];
TotalSeconds = 0;
GameCenterTotalSeconds = 0;
timeSec = 0;
timeMin = 0;
Background = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 480, 320)] autorelease];
Background.image = [UIImage imageWithContentsOfFile:[ [ NSBundle mainBundle] pathForResource:#"Background" ofType:#"png"]];
[self.view addSubview:Background];
timeLabel.textColor = [UIColor redColor];
[self.view addSubview:timeLabel];
NumberLabel = [[[UIImageView alloc] initWithFrame:CGRectMake(0, -4, 60, 70)] autorelease];
NumberLabel.image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"Number" ofType:#"png"]];
[self.view addSubview:NumberLabel];
QuestionNumber = [[[UILabel alloc] initWithFrame:CGRectMake(23, 17, 20, 20)] autorelease];
QuestionNumber.text = #"1";
QuestionNumber.textColor = [UIColor redColor];
QuestionNumber.backgroundColor = [UIColor clearColor];
[QuestionNumber setFont:[UIFont fontWithName:#"Marker Felt" size:30]];
[self.view addSubview:QuestionNumber];
numberLives = 1;
appDelegate = (OppositeMoronTestAppDelegate *)[[UIApplication sharedApplication]delegate];
musicButton = [[[UIButton buttonWithType:UIButtonTypeCustom] retain] autorelease];
musicButton.frame = CGRectMake(5, 283, 35, 35);
musicButton.backgroundColor = [UIColor clearColor];
if (appDelegate.shouldPlayMusic == YES) {
UIImage *Image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"MusicOn" ofType:#"png"]];
[musicButton setBackgroundImage:Image forState:UIControlStateNormal];
[musicButton addTarget:self action:#selector(TurnMusicOff) forControlEvents:UIControlEventTouchUpInside];
} else {
UIImage *Image = [UIImage imageWithContentsOfFile:[[ NSBundle mainBundle] pathForResource:#"MusicOff" ofType:#"png"]];
[musicButton setBackgroundImage:Image forState:UIControlStateNormal];
[musicButton addTarget:self action:#selector(TurnMusicOn) forControlEvents:UIControlEventTouchUpInside];
}
[self.view addSubview:musicButton];
[self showQuestion1];
}
}
try autorelease on your view controller:
GameViewController* game = [[[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil] autorelease];
The navigation controller will take ownership of the view controller passed to it, so you don't have to keep a reference to it. But you can't keep allocating GameViewControllers over and over without releasing them. autorelease is useful for that. You could also release it after you've passed it to the navigation controller if you prefer:
GameViewController* game = [[GameViewController alloc initWithNibName:#"GameViewController" bundle:nil];
[self.navigationController pushViewController:game animated:NO];
[game release];
game = nil;
EDIT:
So if you're already releasing the game object, then it must be a memory leak within the GameViewController class itself.
Annything you alloc, copy or retain in your GameViewController class you're supposed to release in the dealloc method (and maybe also in the viewDidUnload method if you're alloc/copy/retaining in the viewDidLoad method).
The iOS Memory Management Programming Guide might be helpful if you want to get into more detail.
If you want to post the relevant code from the GameViewController class, I'm sure someone will be able to help you pin down the memory leak.
You can also try the Leaks tool in Instruments
EDIT 2:
I'm assuming you have several IBOutlets connected to properties in your GameViewController class...
don't know if you're already doing this, but in your viewDidUnload method AND on your dealloc method you have to set all of these IBOutlets properties to nil in order to release them, like so:
- viewDidUnload
{
//... whatever comes before
self.timeLabel = nil;
self.NumberLabel = nil;
//... etc
}
- dealloc
{
//... whatever comes before
self.timeLabel = nil;
self.NumberLabel = nil;
//... etc
[super dealloc];
}
In general, if you have any properties declared with retain, that means that when you set that property the object will be retained. If you set that property to nil, the object that was there will be released for you. So any properties with the retain keyword should be set to nil (or the backing ivar released).