I'm currently teaching myself Objective-C and Iphone Development using the very good 'Begining IPhone Development'. I have been playing around with one of the sample applications and I am trying to update one button with text from a text field when another button is pressed. I have set up my Actions and links and all that jazz. The code of the single method/function/call is below
-(IBAction)updateButtonPressed
{
NSString *newCaption = [[NSString alloc] initWithString:#"."];
newCaption = tfUpdateText.text;
[btnPressMe setTitle:newCaption forState:UIControlStateNormal];
[newCaption release];
}
It works perfectly the first time I press the button and maybe for two or three times after then crashes. I'm obviously doing something really stupid but I cant see it. This is all I added (as well as declarations, property - synthesize etc). Can someone point out my obvious memory leak.
Update:
If I change to this
-(IBAction)updateButtonPressed
{
[btnPressMe setTitle:tfUpdateText.text forState:UIControlStateNormal];
}
It works fine but could someone explain to me what mistake I was making?
You are incorrectly managing memory. What is the -initWithString:#"." for? You're generating a constant string #".", then leaking it, then pointing to a different string (tfUpdateText.text), then assigning that pointer to the title, then releasing the -text object.
This is both a leak and an over-release. It's the over-release that's crashing.
Perhaps you meant this:
-(IBAction)updateButtonPressed
{
[btnPressMe setTitle:tfUpdateText.text forState:UIControlStateNormal];
}
You have a memory management bug. The newCaption reference object you are releasing is different from the one you initialized. You are accidentally leaking the NSString you allocated, and releasing tfUpdateText.text instead.
You can remove the temperory variable like:
-(IBAction)updateButtonPressed
{
[btnPressMe setTitle:tfUpdateText.text forState:UIControlStateNormal];
}
You're not using NSString properly here (and are really doing a lot more work than required). NSStrings are just pointers, so your second assignment to newCaption is just orphaning the first. When you then send [newCaption release] later on, you're not sending it to your alloc'd object, but rather to tfUpdateText.text, which you didn't retain. Get rid of the alloc and the release, and you should be all set.
Related
I wanted to know that if I am doing correctly, regarding retain and release of digitbutton reference down below. In the book "Programming in Objective C by Stephen Kochan", it says in one section with different example that like digitbutton is holding reference to same object that sender is holding, so in between sender may get released elsewhere and then I would be calling currenttitle method on deallocated digitbutton, if that is the case.
So here I called retain on digitbutton and after its use I called release on it. Well, the example maybe not genuine for this purpose but is this concept valid? Should I make practice to retain objects like in example below?
I am not experienced in writing managed codes.
- (IBAction)clickDigit:(id)sender {
UIButton *digitButton = (UIButton*)sender;
[digitButton retain];
NSLog(#"%#",[digitButton currentTitle]);
[[self displayOutput] setText:[digitButton currentTitle]];
[digitButton release];
}
Update
Yes, the example was wrong; let's say if instead of id sender there is NSString and instead of UIButton, same NSString, and return type is just void. Then do I have to retain sender NSString for safety in case someone holding its reference releases it.
Note: I am not using ARC.
You dont need to retain & release it.
You can do like this
- (IBAction)clickDigit:(UIButton *)sender
{
NSLog(#"%#",[sender currentTitle]);
[[self displayOutput] setText:[sender currentTitle]];
}
What you're doing in clickDigit: is unnecessary.
Instead of me explaining why it is unnecessary, I will point you directly to where I learned proper memory management techniques: Advanced Memory Management Programming Guide
I presume this isn't a common problem, which makes it a bit more difficult to answer. Any help is appreciated though.
I need to call this delegate a number of times in my app, and i noticed that after a number of times, the delegate starts to come back as NULL (and hence stops responding). I put an nslog everywhere the delegate gets called, so i know that at this point, it's fine:
UIImage *image = [self.delegate largeThumnailForMediaAtIndex:indexPath.row];
Then the next time this line gets called, the delegate is set to NULL. No lines around it call or set the delegate. I put an NSLog on the setDelegate method too, and that didn't get called before it changed to NULL.
Any code you might need to see, let me know. Any ideas you want me to try out, let me know about that too.
Thanks for your help.
EDIT: Bizarre, but might help to lead to a solution. I put an NSTimer scheduledTimer.. in the class which gets made the delegate, and got it to fire that once a second so I could see if it turned null at any point. The result i got, however, was that this time it didn't turn null. It returned all of the delegate methods. When i took the timer out, it goes back to returning NULL. Obviously having a timer in there is an odd workaround 'solution'. I'm hoping this rings a bell for someone and gives them a clue to where the problem might lie?
EDIT 2: I've solved this problem by, instead of using this code in my AppDelegate:
JCreateViewController *create = [[JCreateViewController alloc] init];
[create.navigationBar addLeftButtonWithTitle:#"Back" type:JButtonTypeArrow];
create.navigationBar.title = #"Entry #17";
[self.window addSubview:create.view];
Declaring it in my header file, then using this:
self.create = [[JCreateViewController alloc] init];
[self.create.navigationBar addLeftButtonWithTitle:#"Back" type:JButtonTypeArrow];
self.create.navigationBar.title = #"Entry #17";
[self.window addSubview:self.create.view];
I don't understand why this makes a difference though. I'd love to know, if anybody does know?
Looks like you're using ARC. Whatever this object is, nothing owns it. Nothing has a strong reference to it, so it gets released, and then at some point it gets deallocated.
JCreateViewController *create = [[JCreateViewController alloc] init];
This is a local variable. When the variable goes out of scope at the end of the method, you can't access that object anymore. Under MRR, this would be a leak. Under ARC, the object is going to die, just like it was in an autorelease pool.
self.create = [[JCreateViewController alloc] init];
By creating a property (presumably strong) and putting the controller into that property, you've given whatever self is an owning reference to the controller. It will now live as long as the property isn't reassigned or set to nil.
The timer fixed things because the timer retains its target (which I believe was the controller (your question is rather unclear)), and the timer itself is retained by the run loop. So the run loop keeps the repeating timer alive and the timer kept your controller alive.
In short, make sure something owns this object and it'll stick around.
Hi all i have a problem in understanding the concept of retain in following example.i know about usage of retain...but confused here..
i have 2 classes View1 and View2
here is method of View1
-(IBAction)callingView2
{
view2 *view=[[view2 alloc] init];
[self.navigationController pushViewController:view animated:YES];
NSString *ss=[[NSString alloc]initWithString:#"Hi friend"];
[view callingToRetain:ss];
[ss release];
[view release];
}
and in view2 i have 2 methods and str is a string(not allocated)
-(void)callingToRetain:(NSString*)s
{
//[s retain]; //it is not effecting my program
str = s;
}
//And then printing it on a button click after reaching to view2
-(IBAction)print
{
NSLog(#"string = %#",str);
}
The rules says that i should retain the string if i have to use it later, but here it's working without retain.....
I thing this is due to str = s; because it is retaining to MAX_VALUE, but i am not sure...
if this is the problem then does it effects the memory leak concept ?
Any suggestions?
The rule only says you need to retain if you need to use an object later.
It does not say if you fail to retain correctly, it will definitely crash.
Most often, not retaining correctly leads to a crash sooner or later. But your code is an exception, because the string you used was just a constant string known at a compile time.
What's going on is this. Suppose you perform the following operation:
NSString* s=#"foo";
NSString* ss=[[NSString alloc] initWithString:#"foo"];
This in fact makes ss equal to s. As an optimization, the Cocoa runtime does not create a separate NSString instance.
And this object s is a compile-time NSString, which effectively behave like an infinitely-retained object.
That's why your code didn't crash. However, Apple can change the Cocoa runtime implementation in the next version of the OS so that your code does crash.
The point is that you should follow the rule. If you follow the rule, it doesn't crash even in a future version of the OS. If it doesn't, it might not lead the crash immediately, but it eventually will.
Even if it's working without retain, you should use it (or better yet, copy it). That's the rule. That doesn't mean it will work in the future, or with other calls.
Also, don't forget to release it on dealloc.
I have a couple labels I am using as a HUD for the player during a game. I am updating these labels frequently, so that the player has up-to-date information. The problem is is that I've been using
uiLabel.text = [NSString stringWithFormat:#"%3.0f", value];
to pass the new value that the label should have. I have noticed, however, that I have something of a soft-memory leak here. As I'm doing this update multiple times a second and this creates a string that is set to autorelease, I'm ending up taking more memory than I need. And keeping it, as the view is not going away.
I also tried to alloc and release strings explicitly, such as:
NSString* value = [[NSString alloc] initWithFormat: #"%3.0f", value];
uiLabel.text = value;
[value release];
However, I find that this seems to cause the same thing, but faster, though I don't know why. In this situation I would have thought there should never be strings sitting around waiting to be released at all, since I'm so explicitly dismissing them.
Can anyone see what I'm doing here that I obviously am failing to see? Is there a better/more preferred way to handle this? Some cursory searching didn't turn up much for me.
You're not doing anything out of the ordinary. Even with:
uiLabel.text = [NSString stringWithFormat:#"%3.0f", value];
the autorelease pool gets drained every time your code returns control to the run loop (so at least as often as you see the UI updating). If you see growing memory allocations, you should look elsewhere.
I'm going crazy with this my little app... Please help me!!!
this is the source code of the app: Smoking.zip
It only saves a .dat file with an NSMutableArray.
Now, the first time you will launch the app, try to click the cigarette button sometimes: Everything should working fine.
Ok, now close the app, re-open it, and click again on the button. This time the app will crash with the "unrecognized selector sent to instance 0x5d18d60" error.
I was sure the problem was in saving the data, because when i commented the line "[theData writeToFile:dataFilePath atomically:YES];" in the "saveData" method the error disappeared.
Later i discovered that it appears again if i try to read the data from the NSMutableArray.
Please take a moment to check my project and help me, beacause i'm going crazy about that!!
You crazy man, it took quite some time to find these lines:
Cig *oldCig = [mainDelegate.smokeArray lastObject];
...
[oldCig release];
Why are you doing that? You effectively reduce the retain count of the last object in the array to 0. When saving, it is happily saved, with its retain count of zero.
On de-serialization, the decoder will retain any (sub) element it decodes, so the retain count of this last object will, for a brief moment, be 1. Then, on release of the decoder, it releases all elements, and poof goes the last Cig object. When getting that object from the array, you get a pointer pointing to something totally different, and the app crashes.
You should read up on memory handling. lastObject just returns a pointer to an object in the array, without retaining it for you, so you don't have to release it. Furthermore, for functions like
- (NSArray *) quando
try to return an autoreleased array, by calling autorelease before returning:
NSArray *newArray = [[[NSArray alloc] initWithObjects:year,...,nil] autorelease];
Then your other code doesn't have to think about releasing it. And release the dateFormatter. Anything you alloc, retain, copy or new, you must release or autorelease!
easy. On SDK 3.2 and 4.0 you need to make your button functions like this.
// Note it takes one argument UIButton.
- (IBAction) smoke:(UIButton * ) button {
Change this in your .h file and .m file, you dont haven to change anything else. Worked for me.