Just a quick question:
I've got an array, name it sourceArray. This array has dictionaries in it.
I've got a retained property, which is used to hold one item from the array.
When I set the property from the array it works fine.
But, when I set it with an other item, and back to the first, I got a bad_access exception, since the first item has been released.
My question is why? In the source array, items are retained, and when I set the property it retains to. When I set it again, the setter release the old value, and retain the new. I guess... But apparently it doesn't work in this way.
Example:
[self setProperty:[sourceArray objectAtIndex:0]];
[self setProperty:[sourceArray objectAtIndex:1]];
[self setProperty:[sourceArray objectAtIndex:0]]; ---> Exception
I've got it.
In other place, I've a line:
property = [sourceArray objectAtIndex:0];
It looks like when I use the set method, the old value get released, no matter that it hasn't been retained (since in the first time, I didn't use the set method).
My fault...
Related
I'm using a NSMutable String to gain a string back from a database query. I've assigned the variable with #"" and then populate if found from the database. I've tried adding autorelease / release but this causes problems with references to the database call.
Can someone point out my error ?
I would have typed this code but I felt the analyzer arrows were useful.
You're creating an autoreleased NSMutableString and assigning the pointer strDBAAppVer to point to it. But then you throw away the reference to that object, and get the pointer strDBAppVer to point to a new object, an NSString with a retain count of 1.
What I think you want is inside your if statement is something like this:
NSString* databaseField = [[NSString alloc] initWithUTF ...etc.]
[strDBAppVer setString: databaseField];
[databaseField release];
strDBAppVer is first set to [NSMutableString stringWithString:#""], which is reasonable, memory-wise. However, later, you completely re-set the variable to a whole different object, created by alloc/initWithUTF8String:, making strDBAppVer sometimes point to an already-autoreleased object, and sometimes (when the if statement is true) point to an object with retain count +1.
That's why there's a leak but releasing causes issues. If the if statement is true, you've set your variable to point to an object with +1 count, and if it's false, you've set your variable to point to an entirely different object with 0 count.
This looks to me like confusion about mutable strings. Are you aware that, at least in the code posted, you don't actually mutate strDBAppVer? Try this instead:
NSString* strDBAppVer = #"";
Then, inside your if statement,
strDBAppVer = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statementAppVer,0)];
//Now, realizing that strDBAppVer has just been reassigned to point
//to an entirely new object, one created with alloc/init, and therefore one that
//needs to be released,
[strDBAppVer autorelease];
Note the autorelease is only inside the if statement, so it doesn't accidentally overrelease your original value of #"". (Since constant strings shouldn't be released.)
When a method returns an object that is taken from and NSMutableArray does the object must be autoreleased? Check the following method. Also should I autorelease the nil value?
-(NSObject*)getElementByID:(NSString*)ID{
for(int i=0;i<[elements count];i++){
NSObject *element = (NSObject*) [elements objectAtIndex:i];
if([element.key isEqualToString:ID]){
return [element autorelease];
}
}
return nil;
}
You must not autorelease element because you are not an owner of it (you have not put a retain on it). You would have become an owner of it if you acquired it using alloc, new or retain. Since you acquired this object calling objectAtIndex:, you do not own it. See Three Magic Words. Calling autorelease here will cause a crash later do to over-release.
Your method name is incorrect and breaks KVC. A method that begins with get must take a pointer that will be updated with the result. This should be elementForID:. As noted above with the three magic words, naming in ObjC is very important to writing stable code
As a side note, it is traditional to use id is most cases rather than NSObject*. They mean slightly different things, but typically id is correct.
You never need to do any memory management related things to nil. So, no, you should not send autorelease to nil.
You also should not need to send autorelease to the element object that you are returning from your elements array. That object you are returning will remain in memory by virtue of elements itself having retained it. If the calling method would like to retain the value that you return, it may. But if that calling method only uses the returned value within its own scope, it is safe for it to do so without retaining it.
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.
I am trying to update data in a table view using a NSMutableArray. Quite simple :(
What is happening is that I get my data from a NSURLConnection Callback, which I parse and store it in an array and call reload data on the table view. The problem is that when cellForRowAtIndexPath is called back by the framework. The array still shows the correct count of the elements but all string objects I had stored earlier are shown as invalid.
Any pointers
Maybe your problem is something like the below
NSString *value = [NSString stringWithFormat:#"%#",stringFromWebService];
[arrayOfObjects addObject:value];
[value release]; // This should not be released
This may not be exact but could be similar. Here value is obtained from class method of NSString which is an autoreleased object so if you can see it will be released twice, one by autorelease and other by you.
This is the area you need to check.
Check that you are retaining the NSString objects in your NSURLConnection callback. Are you autoreleasing them?
Edit
Ok, forget that last thing. Double checking myself, NSMutableArray will automatically retain the objects when you add them to your array. So you won't need to retain them explicitly:
Like NSArray, instances of
NSMutableArray maintain strong
references to their contents. If you
do not use garbage collection, when
you add an object to an array, the
object receives a retain message. When
an object is removed from a mutable
array, it receives a release message.
So you need to check you aren't doing any other explicit releases on the objects you are adding to the array. Are they referenced anywhere else?
The problem is there where you are adding the string object to a mutable array. The string object was already invalid that time. That's why the time you are accessing them from the array, they are invalid or do not exist.
So best thing is to check the code where you are adding the string object during the parsing.
Your problem may be that you have not initiated the array correctly.
Are you using the array as an attribute of the class?
Make sure when you load the view you allocate and initiate the array.
I have what I think is a weird error but of course i'm relatively new to iPhone Development so it's possible that it's not all that weird after all.
I have an array (NSMutableArray) of objects that I am keeping track of (one is added to the array every time the user touches a button) what I'm trying to do is when the array reaches a certain value I add the new object to the beginning of the array and then remove the last object in the array. When I step through my code everything works the object is removed but then the app just crashes...the debugger isn't on any line of code when the app crashes and there are no loops or timers in the app yet so I can't think of anything else that is running.
here is the code that is executed right before the crash
if([objectArray count] > 10)
{
MyObject *objectToRemove = [[MyObject alloc] init];
objectToRemove = [objectArray objectAtIndex:10];
[objectArray removeObjectAtIndex:10];
[objectToRemove removeFromSuperview];
}
the main point of this code is that everytime the user touches a button an object is added to the screen and displayed, and then when the number of objects reaches 10 and the user touches the button again the first object that was added is removed and the new object is displayed. If I comment out the removeObjectAtIndex line everything works as intended but the array continues to grow.
I've also tried removing the object after the UIview is removed and the app behaves the same way. If I try to remove an object from the array at a different index (I.E 3) the app doesn't crash but it doesn't give me my expected result. but like I said the code runs fine and when I check the count of the array before and after the execution of the line the value is 11 and 10 respectively.
Any help you can provide would be appreciated,
BWC
I don't think this does what you think it does. Going line by line:
MyObject *objectToRemove = [[MyObject alloc] init];
You allocate a new object of type "MyObject.
objectToRemove = [objectArray objectAtIndex:10];
You overwrite your local MyObject pointer with whatever was at index 10 in objectArray. The objectToRemove that you initially allocated is now leaked.
[objectArray removeObjectAtIndex:10];
Now you've removed the object at index 10. When you remove it from the array, it is released and its reference count is decremented. This may (or may not) cause it to be deallocated.
[objectToRemove removeFromSuperview];
Now you are sending a message to the object that was previously in the objectArray. If the object was deallocated by the previous line of code, I would expect a crash.
Putting aside the leak in the declaration, you might be able to do this:
objectToRemove = [[objectArray objectAtIndex:10] retain];
[objectArray removeObjectAtIndex:10];
[objectToRemove removeFromSuperview];
[objectToRemove release];
This would prevent the object from being deallocated out from under you, if that's why the crash is happening.
One way to determine if my scenario is what is happening is to turn on NSZombies, which you can do by setting the environment variable NSZombieEnabled to YES.
How about:
MyObject *objectToRemove = [objectArray objectAtIndex:10];
[objectToRemove removeFromSuperview];
[objectArray removeObjectAtIndex:10];
removeFromSuperview will decrement the retain count once on objectToRemove. (removeFromSuperview implies that it was added to the view previously and that had retained it and incremented its retain count by one). removeObjectAtIndex will decrement it again. But in this order, you most likely will not be releasing it when you still need it around. (Of course, adding to the array also retained it so the order may not be that important).