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.
Related
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.
I'm new, but I read a lot about memory management, and really tried to find the answer myself.
It sounds so basic and yet I apparently don't get it.
I have a NSMutableArray as property in .h:
#property (nonatomic, retain) NSMutableArray *documentListArray;
I synthesize it and release it in (void) dealloc.
To populate the array, I have a method - (void)updateRecordList and in there:
self.documentListArray=[DocumentDatabase getDocumentListSortedByDate];
EDIT:next line:
[[self recordListTableView] reloadData];
where DocumentDatabase is a different class with the class methods getDocumentListSortedByDate and getDocumentList.
Here is what is happening in getDocumentListSortedByDate:
NSMutableArray *returnArray = [DocumentDatabase getDocumentList];
//sorting...
NSLog("array count:%i",[returnArray count]); //returns correct numbers first and second time
return returnArray;
and in getDocumentList
NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:files.count];
//add file data to array...
return returnArray;
This works the first time I call updateRecordList, but after adding a file and calling updateRecordList a second time, it crashes with (using NSZombies):
* -[NSCFNumber dealloc]: message sent to deallocated instance 0x7504c90.
With a lot of logging I narrowed the problem down to the line above in updateRecordList and it works if I change it to:
self.documentListArray=[[DocumentDatabase getDocumentListSortedByDate] retain];
My conclusion is that the array down in getDocumentList has been autoreleased before it arrives. So my questions are:
1. Why do I have to retain it there? Shouldn't that happen by itself by declaring the property (retain)?
Or, in other words, why is the array autoreleased too early (assuming this is what is happening)?
2. When I assign a new array to self.documentListArray, is the old array automatically released? If I try to release it myself before getting a new documentList, it crashes too.
Thanks in advance for any reply.
EDIT:
Maybe I'm an idiot: I failed to mention that documentListArray is the data source for an UITableView (see the added line on top). I suspect that I am doing something wrong with populating the table view, and the array gets retained...? It does however crash on assigning the property, not on reloadData.
I go back to study if I use the UITableViewDataSource protocol properly. Thanks to everybody, your answers brought me hopefully on the right track. Will update when solved.
EDIT2:
It works now without retaining, and I think I understand why: I debugged extensively and found that Objects contained in Objects added to the array where nil. Particularly, deep down in encodeWithCoder I did not use "self" when assigning values. When decoding, those values where nil. Since I changed that, it seems to work.
I suspect that not assigning the new array caused the crash, but the TableView which would read the new array-even before I call reloadData. Which would lead back to Davids question of synchroneous access. Thank you all for your help.
The code you've shown it appears correct; it should not be necessary (or correct) to call retain yourself, as long as you are assigning the value to a property with retain semantics before the autorelease pool is drained. Are all the calls (getDocumentListSortedByDate, getDocumentList) happening synchronously, or are you doing any of this in the background? Double-check that you're assigning using the "self." ("self.documentListArray =") instead of just assigning directly to the instance var ("documentListArray ="); if you omit the "self.", the setter is bypassed.
No, don't free the old value before assigning; that's the setter's job.
Hi I am getting memory leak in Instruments for the following line of code .
NSArray *itemsList=[[NSArray alloc] initWithObjects:#"Love",
#"Hate",#"Happy",#"Sad",
#"Desire",#"Anger",#"Hope",#"Fear",#"Silly",nil];
I am using the below code:
arrayList is also released in dealloc block.
NSArray *itemsList=[[NSArray alloc] initWithObjects:#"Love",#"Hate",
#"Happy",#"Sad",#"Desire",
#"Anger",#"Hope",#"Fear",#"Silly",nil];
self.arrayList=itemsList;
[itemsList release];
I'm assuming that arrayList is declared using retain in the #property statement. If not, then that is certainly your problem.
If it is, then you have a leak, but not in the code you've posted. It's important to realize that Instruments first shows not necessarily where the leak occurred, but where the leaked memory was allocated. You'll have look through the rest of your uses of arrayList and find where you have a retain that's missing a release.
If you click on the arrow next to the memory address of the object in Instruments, you should be able to see everywhere that your object was retained and released. You'll have look through them and identify which retain is missing a release.
I decided to start using removeAllObjects so that I could re-use some of my NSMutableArrays. However, I am finding that the call to removeAllObjects causes the array to get released and so it crashes when the viewController is displayed for the second time.
I've looked on the web and it seems that some people are saying that removeAllObjects just clears their array, while others are saying that it also releases the array. Which is true? It appears to release for me, but I find that weird and would expect the behaviour to be just to release the objects within the array. Nothing in the documentation warns of this, which I also find strange.
EDIT: With NSZombies turned on, all I get back is:
-[__NSArrayM removeAllObjects]: message sent to deallocated instance 0x625e4e0
Basically telling me that because the array was released due to the removeAllObjects, it can't call removeAllObjects second time round... Argh!
Can anyone help please?
Thanks!
removeAllObjects releases the objects in the array but has no effect on the array itself (except to make it empty).
Unless
one or more of the objects in the array had a retained reference to the array itself.
Okay so I was being an idiot!
I was doing:
[myArray removeAllObjects];
myArray = someOtherObject.someArray;
therefore resetting the pointer to some other object. I've now changed it to this:
[self.myArray removeAllObjects];
[self.myArray addObjectsFromArray:someOtherObject.someArray];
And it's now okay. I'm an idiot! Sorry all, thanks for your help!
removeAllObjects does not release the array itself, as you rightly expect. If it is being released the problem is somewhere else. You can use the NSZombie's instruments to check where it is being released.
If you are autoreleasing your MutableArrays, they might be released when they are empty as they are after a call to removeAllObjects. Use NSZombies to look where it is released exactly.
NSZombies can't tell you where the object is being released, so it shows you the first time you send a message to the deallocated object.
Work backward from that call to -removeAllObjects, and you'll find the bug.
[yourArray removeAllObjects];
it seems that the element of yourArray will release aotuomatically
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.