I have a project with ARC.
I have an NSArray whit some object inside.
At certain point I need to change the object in the array.
Whit a NSMutableArray I'll do :
[array removeAllObjects];
and I'm sure that this method release all object contained in the array.
But with an NSArray I can't do that! So, my question is: if I set array to nil and then re-initialize it, the old object contained in the array are really released from memory ?
array = nil;
array = [[NSArray alloc] initWithArray:newArray];
Or I need to use NSMutableArray ?
You can just do this:
array = newArray;
This will cause array to be released. When this NSArray gets deallocated, all contained objects will be released, too.
The old array will be deallocated when there are no more strong references to it. If you had the only strong reference to it, then when you set array to something else, it will be deallocated immediately.
When the old array is deallocated, it will release all of the objects it contains. If there are no other strong references to those objects, they will also be deallocated immediately.
You don't have to set array = nil before setting it to the new array.
I would suggest NSMutableArray because there would not be overhead of allocation and deallocation again
Related
I've made a NSMutableArray a property on my view controller, which holds some core data objects for users.
When the user presses a button , I clear out the contents of the mutable array with a while loop,
while (self.mArray.count != 0){
[context deleteObject:self.mArray[0]];
[self.mArray removeObjectAtIndex:0];
}
After the while loop, I reinit the array:
self.mArray = [[NSMutableArray alloc] init];
I know it isn't necessary since there should be zero objects left in the array, but regardless, when I reinitialize the array, and then check the class of the array in the debugger, I get __NSArrayI, which is corroborated by an exception thrown when I attempt to add an object into self.mArray right afterwards.
I've looked for any other references to my array, but I've always passed around [self.mArray mutableCopy] as arguments to other methods, and I never cast it as an NSArray. I just don't understand how calling [[NSMutableArray alloc] init] would initialize the array as an immutable array.
What am I missing?
It's likely that you have declared mArray as copy in the property declaration. Change that to strong.
So let's say I have an NSMutableDictionary filled with objects like this:
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
for (int i=0; i<10; i++) {
someClass* tmp = [[someClass alloc] init];
[dict setObject:tmp forKey:[NSString stringWithFormat:#"%i", i]];
}
If I set the dictionary itself equal to nil, does that release all the objects I put in it (thus freeing the allocated memory for each one)?
I want to use an NSMutableDictionary as a cache basically, and whenever my app receives a call from the system to didReceiveMemoryWarning, I want to clear out this cache and release every object in it.
So is setting the dictionary to nil enough, or do I have to call some method to wipe out the data and free all the memory allocated by creating someClass instances?
If there is no another references to dictionary
dict = nil;
will cause dict deallocation. Deallocation method will be called - [NSMutableDictionary dealloc]. And inside this method all references to its content will be released.
You also cal call NSMutableDictionary's
- (void)removeAllObjects;
method instead.
I also suggest you to look at NSCache class - it already handles didReceiveMemoryWarning itself:
An NSCache object is a collection-like container, or cache, that
stores key-value pairs, similar to the NSDictionary class. Developers
often incorporate caches to temporarily store objects with transient
data that are expensive to create. Reusing these objects can provide
performance benefits, because their values do not have to be
recalculated. However, the objects are not critical to the application
and can be discarded if memory is tight. If discarded, their values
will have to be recomputed again when needed.
and NSMapTable:
NSMapTable is a mutable collection modeled after NSDictionary but
provides different options.
I want to clear my array, what i did is,
I have tableview view in my app, first i am fetching data from server and loading it in tableView.
-(void)viewDidLoad{
//fetching data from server using background thread and storing it in array called (msg_array)
[table reloadData];
}
when last row comes on screen i want to fetch new data from server and i want to display it,
-(void)LoadMoreData{ //this method gets fire when last cell is on screen
if ([msg_array count]>0)
{
[msg_array removeAllObjects]; //crashes here
}
}
This gives the error:
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[__NSArrayI removeAllObjects]: unrecognized selector sent to instance
Why does it cause a crash:
The array is allocated like this:
msg_array = [dictShow copy];
dictshow contains the data and copying it to msg_array and dictshow is mutabledictionary
(Taken from comments)
'-[__NSArrayI removeAllObjects]: unrecognized selector sent to instance
This means that the array doens't have the method you're trying to call. That's because it is an immutable array (NSArray), not mutable (NSMutableArray).
Either make it mutable if you want to mutate it. Or, replace:
[msg_array removeAllObjects];
with:
msg_array = #[];
Based on your comment, the array should be mutable. That means that you have a mutable property / instance variable, but that you're creating an immutable instance to store into it. Find that location and update it (to create / return a mutableCopy at least).
Its because you are trying to modify a immutable array, you have two options here:
msg_array = #[];
OR
NSMutableArray *mutableMessageArray = [msg_array mutableCopy];
[mutableMessageArray removeAllObjects];
msg_array = [mutableMessageArray copy];
I prefer the first option as its neater, but if you need to do any other modifications of the array the latter option might be best for you.
NB:
Check how you declare msg_array, can you post that code?
msg_array could be immutable thats why it is crashing. removeAllObjects is only for NSMutableArray
__NSArrayI, looking carefully at this bit, we can see it's suffixed with an i. This i means the array is immutable and can't be changed.
You possibly want to use an NSMutableArray
msg_array = [dictShow copy]; dictshow contains the data and copying it to msg_array and dictshow is mutabledictionary
This is very strange! I expect that calling copy on a dictionary would always return a dictionary. Unless you are mistaken there, I can only imagine that either the dictionary's keys or its values are being returned.
I think you might have meant mutable array; assuming you did, the call to copy returns an immutable object, try [dictShow mutableCopy] instead
msg_array = [NSMutableArray arrayWithArray:_MoodsArray].mutableCopy;
I am doing some research on reference count increase. Please help on finding it.
Below is sample code and research i'm doing what would happen of reference counting for each line below.
.h file
NSArray *tempArray;
#property (nonatomic, retain) NSArray *tempArray;
.m file
#synthesize tempArray;
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // Thinking reference count increases to "1"
tempArray = myArray;// reference count increases and tempArray gets retain count "1" now.
tempArray = myArray;// reference count increases and tempArray gets retain count "2" now.
tempArray = [NSArray arrayWithObject:#"SomeString"]; // retain count = ?
}
I know this code may not be for functioning, but this is for only researching about what will happen on reference counting for such scenarios. I tried printing retainCount, but it doesn't show the correct result. Please advise me how does the reference count works on this each line?
In lines 2, 3 and 4 you are affecting the instance variable tempArray to the same object as myArray. But if you write it this way, you try to affect an instance variable. As a matter of fact, if you didn't write any #synthesize tempArray or #synthesize tempArray = tempArray in your code, by default the instance variable generated automatically to store the property value is the same name as the property name, but prefixed with an underscore. So as the property name is tempArray, the instance variable is named _tempArray. The instance variable tempArray itself does not exist and your line of code is invalid.
So if we suppose you wrote instead:
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // (1)
self.tempArray = myArray; // (2)
self.tempArray = myArray; // (3)
self.tempArray = [NSArray arrayWithObject:#"SomeString"]; // (4)
}
In (1) you are creating a brand new instance of NSArray. "alloc" always initialize new instance with a reference count of 1
In (2) you write self.tempArray = myArray (which is equivalent to [self setTempArray:myArray]; and thus call the property setter), so you set the property to point to the same array you created in (1). This array is thus retained by the property, and its retainCount increses by one, because it is retained by myArray and by the self.tempArray property.
In (3) you affect the property to the very same object as before. This the ref count does not change at all. You could understand that as if you replaced the value of the self.tempArray with another value, so the setter of the property release the old value (decrementing its ref count), then retain the new value (thus incrementing its ref count). As in your case the old and new values are the same object, you would decrement the ref count of your array then re-increment it again. In practice, the ref count does not even change at all (instead of decrementing+incrementing again) to avoid any potential dealloc of the object, because the default implementation of a property setter is as follow:
-(void)setTempArray:(NSArray*)newValue
{
// check if old and new value are different. Only do sthg if they are different
if (newValue != _tempArray)
{
[_tempArray release]; // release old value
[newValue retain]; // retain new value
_tempArray = newValue; // store new value in the backing variable associated with the property
}
}
In (4) you replace again the value of the property tempArray, but this time with a completely new object. So the property will release its old value and retain the new one. Thus the first array you created in (1) which had a refcount of 2 (retained by myArray and by self.tempArray) decrease its ref count to 1 (because the property won't retain it anymore), and the new instance you created [NSArray arrayWithObject:#"SomeString"] is retained by the property, so its ref count is +1.
If you replaced self.tempArray = ... (so the use of the property) with the direct use of the instance variable, using instance variables don't retain the objects they are affected to (except if you are using ARC but it seems you don't), so the ref count of the object wouldn't have changed at all in (2), (3) and (4).
Actually, retain count increase only in new, alloc, retain and copy condition but if we are providing ownership to an object through this for retain count will increase other than that there is no possibility to increase retain count.
First things first, don't even try to rely upon retainCount.
After that: you're wondering which scenario happens among the ones you enumerated. Well, neither one.
Why? Because, in first palce, you're assigning to an instance variable directly - that won't change retain count. At all. Except if you use ARC, but it seems you don't.
You probably wanted to assign stuff to the property of the object, that is, write
self.tempArray = myArray;
etc. Now because the property itself (and not its backing ivar!) is declared as retain, the corresponding accessor method will increase the reference count of the object being assigned to the property. However, in order not to leak memory, an accessor method is usually implemented by releasing the previously assigned object when assigning and thus retaining the new one, i. e.
- (void)setTempArray:(NSArray *)tmp
{
[tmp retain];
[tempArray release];
tempArray = tmp;
}
So basically, when you reassign myArray to the self.tempArray property, it looses and gains a reference, thus its reference count doesn't chnage at all.
When you assign another, new array to the property, then again myArray loses a refcount, dropping to 0 it is deallocated, then the new array, created using + [NSArray arrayWithObject:] is retained. Its exact reference count is supposed to be 1 after this, since it was created using alloc - init - autorelease (that's how the method is implemented), and it has been retained by the property. However, the value returned by - retainCount is still (and never) to be relied upon.
In your particular example, you are assigning to tempArray directly and not self.tempArray, so the retainCount will stay at 1 throughout. But let's go through what would happen if you did what I think you meant.
In objective-c, a synthesized retained property will have a setter functionally equivalent to this:
-(void) setTempArray:(NSArray *value)
{
if(tempArray != value) {
[tempArray release];
tempArray = [value retain];
}
}
This increases the retain count when a new object is assigned to it, essentially does nothing when it is set to the same object, and releases it when something else is assigned to it. So the retain counts in your example go something like this:
-(void) sampleFunction
{
NSArray *myArray = [[NSArray alloc] init]; // Retain count of 1
self.tempArray = myArray; // 2
self.tempArray = myArray; // still 2
self.tempArray = [NSArray arrayWithObject:#"SomeString"];
// myArray.retainCount is 1,
// tempArray.retainCount is 2 but with 1 autorelease
// myArray leaks
}
I am trying to create an NSMutableArray using arrayWithArray, add two objects, sort, and store to an ivar as an NSArray. My code looks like this:
NSMutableArray *_mutableItems = [NSMutableArray arrayWithArray:[self.mainViewController.someDictionary allKeys]];
[_mutableItems addObject:#"Buildings"];
[_mutableItems addObject:#"Parking"];
self.curItems = [_mutableItems sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
When I profile the app I get a memory leak for an NSArray after the view is popped. But what I don't understand is: aren't all of these objects autoreleased? Am I increasing the retain count when I assign it to the instance property?
Yes, setting the property is probably increasing the retain count. Specifically, _mutableItems will be autoreleased, but the array you create with sortedArrayUsingSelectoris retained by the property.
Does your property declaration include retain or copy?
#property (retain) NSArray *curItems;
If so, in your class dealloc method, make sure you call release on the array;
- (void)dealloc {
[curItems release];
[super dealloc];
}