I have a NSMutableArray holding a whole bunch of UITextFields which I have created and allocated memory.
In my viewDidUnload method I need to release this memory. How do I do it?
for(int i = 0; i < [arr count]; i++){
UITextField* txtField = [arr objectAtIndex i];
txtField = nil;
}
arr = nil;
Will this work? or do I just need to set arr = nil;?
I am using ARC so i set to nil not release.
If the array is an instance variable of the view controller, as long as you're using ARC it will automatically be deallocated when the view controller leaves memory.
If you need to manually remove the array, set it to nil.
arr = nil;
If you need to reuse the array later, you will need to reallocate it after setting it to nil.
Assuming your array is an #property of your object, a good trick is to allocate the array in the getter:
-(NSMutableArray*)arr {
if (!_arr) {
_arr = [[NSMutableArray alloc] init];
}
return _arr;
}
This way you always get an array when you try to access it, even if it's previously been set to nil.
Let's see what your code does to understand what happens:
UITextField* txtField = [arr objectAtIndex i];
that makes a copy of the value in the array, and values in an NSMutableArray are references, and stores that value in a variable txtField. As txtField is defined, implicitly, to hold strong references ARC will (subject to any optimisations) register an ownership interest in the reference (aka "retain"). Your next line:
txtField = nil;
stores the nil reference value in txtField. As txtField holds strong references any store causes ARC to relinquish ownership interest (aka "release") in the previous reference value stored in the variable.
The array is never changed. You've iterated over its contents, copied each value, retained that value, released that value. Finally you write:
arr = nil;
which stores the nil reference value in arr. As arr holds strong references any store causes ARC to relinquish ownership interest (aka "release") in the previous reference value stored in the variable - and that previous value was your reference to your NSMutableArray. If there are no other owners of the array it is destroyed, and when an array is destroyed it relinquishes its ownership of any values it contains - which in this case are your UITextField instances, and if there is no other owner of those then they are destroyed...
So at most all you need is:
arr = nil
but you may not even need that. As arr holds strong references when its lifetime ends - at the end of the block or method containing its declaration if a local variable, or when the instance is destroyed if an instance variable - then ARC will relinquish its ownership interest, etc...
HTH
When using ARC, you release a variable by setting it to nil just as you have done with arr = nil. Note that the memory will only be freed when all pointers to the object have been set to nil, so make sure you aren't holding on to them anywhere else.
Secondly, if you are running into memory issues you should be handling this in the didReceiveMemoryWarning method, as viewDidUnload is no longer supported on iOS 6.
Related
#implementation GroupedInexedViewController
{
NSDictionary *names;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyList"
ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
names = dict;
[dict release];
}
Is deallocating 'dict' affects 'names'? I mean does it deallocate 'names' too? I saw in another post that its a bad practice? But why?
Edit: ARC is disabled.
In short, if you are not using ARC, yes: deallocating dict will affect names. This is because you are assigning the names pointer to the single NSDictionary you have allocated.
If you wanted to have names retain the NSDictionary when you dealloc dict, you would need to send dict a retain message:
names = [dict retain];
Since you're manually calling release, I'm going to assume you're not using Automatic Reference Counting (ARC).
There is some terminology mix up here.
It doesn't deallocate names. You're decrementing the reference count of dict when you call release. Once that reference count hits 0, the memory will be deallocated.
The problem is you assigned dict to names without first calling retain on dict.
Retaining an object increases it's reference count.
You can either choose to make *names a property, which will handle the memory management for you, or you can manually increment the reference count by calling retain: names = [dict retain];
If you do this, you must also implement a dealloc method and release names inside the dealloc method.
Your code assigns names with the value of dict. dict is a pointer to an object, so when you assign its value to another pointer (names), both pointers are referencing the same object and can be considered identical.
So yes, when you release dict, you are also releasing names.
BTW, you can assign to names directly without going through dict:
names = [[NSDictionary alloc] initWithContentsOfFile:path];
And if you can enable ARC, you never need to worry about releasing objects.
No answer so far seems to mention the difference between ARC and non-ARC (MRC) usage and the difference between properties and instance variables.
First of all, properties are really just setter and getter methods backed by an instance variable. When you set a property like self.dict = someObject; and the property was declared as strong or retain, then someObject is retained.
However, if you only have an instance variable (not a property) and you're using MRC, then merely writing dict = someObject; duplicates the pointer only but does not increase the reference count - if you write [someObject release] after this, you should assume that dict is invalidated as well (even if the object pointed to by these two pointers is not actually deallocated - this is a rule of reference counting).
If you are using ARC, then assigning to a variable increases the reference count by one as well - so if someObject has a reference count of 1, then writing dict = someObject; will increase the reference count of the object (now pointed to both by dict and someObject) to 2.
dict and names are two different variables, do not mix them!!!
"release" only when you alloc, copy, mutablecopy, retain.
Otherwise if gets created in autorelease mode.
And if you are using ARC, then no need of "release" compiler will take care of all these newly allocated spaces.
I wrote a class, which acts as a filter. I pass three objects:
An NSArray, which holds objects to filter (these objects have a timestamp property)
An NSMutableArray (which will hold the section names for a tableView, the periods based on timestamps). I need this array, because I have to sort the periods.
An NSMutableDictionary, in which the keys will be the section names, the values are NSMutableArrays, which hold the items for a given period.
In the class from which I pass these objects, there is a tableView, in which I display the items.
This class has it own NSMutableArray and NSMutableDictionary, I not initialize them, only retain the corresponding return values of the filter class. In the delloc method I release them. There is a method in the filter class:
+ (void)insertItem:(id)item forPeriod:(NSString *)period toContainer:(NSMutableDictionary *)container {
if ( ![[container allKeys] containsObject:period] ) {
// the period isn't stored, create and store it
NSMutableArray *periodArray = [[NSMutableArray alloc] init];
[container setObject:periodArray forKey:period];
[periodArray release];
periodArray = nil;
}
// store the item
NSMutableArray *arrayForPeriod = [container objectForKey:period];
[arrayForPeriod addObject:item];
arrayForPeriod = nil;
}
The instruments shows me leak when I set the newly allocated array as an object of the dictionary. At this point this is definitely true, because the dictionary retains again the array, so after the release, it retain count remains 1. But I think in the caller class when I release the dictionary, the array will be released too. Am I wrong?
Yes it is considered as a leak because your var is a local variable. Then you still have an object in memory but no reference to it. Remember the init makes a retain + the retain made by the dictionary = 2 retains. Just create your array using
NSMutableArray *periodArray = [[[NSMutableArray alloc] init]
autorelease]
Is it clear ?
You could switch to ARC. Alternatively, check what the static analyser thinks of your code. It is pretty good at finding memory leaks, better than most humans.
Once you have a few hundred objects in your dictionary, you waste an awful lot of time and memory. A dictionary doesn't have an array of all keys stashed away somewhere, it has to create it every time you call your method. That's copying a few hundred pointers (cheap) and retaining them (expensive). containsObject for an array compares the object with every object in the array calling isEqual: That's expensive. It's an NSString compare each time. The array is autoreleased, and when it finally goes away, all the keys in it get released. Again expensive.
NSDictionary uses a hash table, so [objectForKey ] will immediately go to the right object. One operation instead of possibly hundreds.
In my app, the singleton class (SharedData) allocates memory for a NSMutableArray:
[self sharedMutableArray] = [[NSMutableArray alloc] init];
Class A populates the this sharedMutableArray:
NSObject *obj = [NSObject alloc] init];
[sharedMutableArray addObject];
obj = nil;
Class B does this - and that's my question:
NSMutableArray *tmpArray = sharedMutableArray;
... uses the tmpArray locally
[tmpArray removeAllObjects];
tmpArray = nil;
This is an inherited code and my hunch is that this is a NO-NO. Can some one confirm that assigning nil to tmpArray will release memory for sharedMutableArray also.... I guess the author wanted to release tmpArray only...
Assigning nil to tmpArray only sets your pointer to the object to nil. It does not affect the object itself (or its lifecycle) at all. In this case, setting the objects you've created to nil does nothing, since their variable declaration is in local scope - if you want the objects to be deallocated from memory you need to send them release before setting the pointer to the object to nil.
However, sending removeAllObjects is affecting your original sharedArray, because you didn't copy the array, you simply set a new pointer to point to the 'singleton'. You probably want this:
NSMutableArray *tmpArray = [NSMutableArray arrayWithArray:sharedMutableArray];
You won't need to use removeAllObjects in the above case because it will be autorelease'd. I suggest you read this.
tmpArray is a pointer, and it's initialized to point to the same mutable array that sharedMutableArray points to. For that reason, the line:
[tmpArray removeAllObjects];
will empty out the array, and anyone using sharedMutableArray will see that change. In other words, the assignment
NSMutableArray *tmpArray = sharedMutableArray;
doesn't make a copy of the array itself, it only copies the pointer. Any messages you send using that pointer will go to the shared array. Likewise, assigning nil to tmpArray sets the pointer tmpArray, but doesn't do anything to the array itself.
Finally, setting a variable to nil never releases memory. Setting a property to nil, on the other hand, will release memory under some conditions (e.g. when the property is declared to retain its contents). You're setting a variable here, not a property, so there's no chance that the array will be released.
I thought that NSArray/NSDictionary/NSSet and their mutable subclasses just added the pointer to the object, and not the object it self.
So if set my "simple" object to nil after I added it to the container, why isn't the reference nil also in the Array (container)?
Here is the code:
NSMutableArray *array = [[NSMutableArray alloc] init];
Simple *simple = [[Simple alloc] init];
[array addObject:simple];
//Array sends retain, lets release
[simple release], simple = nil;
NSLog(#"Simple = \"<Simple: %p>", simple);
NSLog(#"Array: %#", array);
[array release], array = nil;
Here is the output:
2011-02-16 20:00:03.149 Allocations[5433:207] Simple = <Simple: 0x0>
2011-02-16 20:00:03.150 Allocations[5433:207] Array: (
<Simple: 0x4d3d4e0>
)
NSArray adds a pointer to the object. In order to track changes to variable, the array would have to add a pointer to the variable itself (remember, you're just setting the variable to nil, not the object). There can be many variables all pointing to the same object, and reassigning them won't change any others.
Remember: Pointers aren't magic. They're just ordinary variables whose value is a memory address — in this case, the memory address of an object. Two pointers to the same object aren't "linked" any more than two ints with the value 5. Changing the pointer doesn't affect the object; in order to affect the object, you have to either send it a message that causes it to change (e.g. [object setValue:6]) or dereference the pointer to access the object's members directly (e.g. object->value = 6).
PS: Don't access an object's members directly. It's bad and fragile and very prone to bugs. I just mentioned it here to explain how pointers work.
Setting simple = nil just makes that pointer point to nothing. It doesn't delete the object that the array still has a pointer to. At the point of your NSLog statements, the retainCount of the Simple instance that simple pointed to would be one.
Create simple
simple => (Simple instance: retain count 1)
Add to array
simple => (Simple instance: retain count 2)
[array objectAtIndex:0] => (Simple instance: retain count 2)
Release simple
simple => (Simple instance: retain count 1)
[array objectAtIndex:0] => (Simple instance: retain count 1)
Set simple = nil
simple => nil
[array objectAtIndex:0] => (Simple instance: retain count 1)
Release array
(Simple instance: retain count 0, subsequently destroyed)
NSArray does contain only a pointer to the object that is added, but that's ok -- it's not pointing to the simple pointer itself, but rather to the Simple object that simple pointed to. Thus in your example, after you change what simple points to, the array is still pointing at the original Simple object.
I'm trying to populate a dictionary dynamically in a for loop like so:
pseudo-code
myObject = new Object
myDict = new Dictionary
for(int i; i < 10;i++)
myObject.value1 = new data from somewhere
myObject.value2 = new data from somewhere
myDic = value:myObject key:i
end for
So my question is in Objective-C, can I just keep assigning new data to the myObject without releasing it every time in the loop? I'm still trying to figure out memory management in Objective-C.
That depends on what myObject actually is and how you have its properties defined. Assuming you have them defined to release such as here:
#property (nonatomic, retain) SomeClass *myProperty;
then yes, you can do that. The setters synthesized automatically for you by objective-c release and set to nil old values of properties before retaining and assigning new values.
However, although there is no problem with your memory management, there is still a problem with your code. Remember that myObject is a pointer, just like in C or C++. That means that if you add it to myDic and then modify the properties of the object later (such as on the next iteration of your for loop), those changes will be reflected when you pull the object out of the dictionary at some point in the future. What you want is something like this:
myObject = nil
myDict = new Dictionary
for(int i; i < 10;i++)
myObject = new Object
myObject.value1 = new data from somewhere
myObject.value2 = new data from somewhere
myDic setValue:myObject forKey:i
myObject release
end for
What this does is release myObject after putting it in the dictionary (all cocoa collection classes retain any object you put into them and release the objects when they are either removed or the collection itself is deallocated) so you don't have a leak, as well as allocate a new instance of Object at every iteration so you aren't modifying the same object over and over again.
If you haven't been reading Apple's Memory Management Guide for Cocoa, I'd highly recommend doing so. It's very informative.
Memory management in Objective-C is done through reference counting. When you allocate an object, it has a reference count of 1. The 'retain' method increases the reference count while 'release' decreases it. When the reference count reaches 0, the 'dealloc' method is called (you should never call 'dealloc' explicitely) and the object is freed.
You can also call 'autorelease' on an object, which will decrease the reference count "some time later". This allow you to make use of the object without worrying about releasing it.
For your question. When you add an object to a container, a 'retain' call is made on the inserted object. This means that you have to 'release' the inserted object:
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
for( int i = 0; i < 10; ++i ) {
Object *myObject = [[Object alloc] init];
[myDict setObject:myObject forKey:[NSNumber numberWithInt:i]];
[myObject release];
}
You could also use:
Object *myObject = [[[Object alloc] init] autorelease];
and you wouldn't have to call 'release' after the insertion.
Your question is very open, it depends on how your Object and Dictionary is implemented.
First, you allocate object only once and release it in loop 10 times -> memory problem.
If we get past that. Assume that you allocate new object in every iteration of loop.
Take NSMutableDictionary and MyObject that extends NSObject as an examples of your Object and Dictionary.
When you call setObject:forKey on NSMutableDictionary instance, the object will receive retain call so dictionary keeps reference of it.
When you release it at the end of iteration the dictionary still keeps reference to it so it is ok.
Another thing to keep in mind if you use this in a big loop:
Object *myObject = [[[Object alloc] init] autorelease];
is the fact that autoreleased objects go to autorelease pool. Pool gets cleaned at the end of current event processing. If you create lots of objects it can take a lot of time to get through it at the end of event processing. In that case you might chose to create your own autorelease pool only for the loop - but I guess that's more advanced topic.
Definitely have a look at some Objective-c and memory management references from Apple.
What you're doing works with only one object and puts that one object into the dictionary ten times. It does not put ten separate objects into the dictionary.