What happens if I call [alloc] init] on an object which already was initialized and alloc'ed?
In my particular case I have an NSMutableArray which I initialize in superclass Parent using NSMutableArray* someArray = [NSMutableArray alloc] init];
In subclass Child I need to insert an object in someArray but at a specific index, for example 3.
So if the array has no items, or if it has less items than the index I'm trying to insert at (array has 4 items, and I want to insert at index 10) it will crash.
What would happen if I initialized someArray again in Child class? Would the pointer stored in someArray be replaced with the new one I'm initializing and the "old" one would just leak?
EDIT:
Sorry, my terminology was a bit off. I don't mean doing [someObject alloc], but doing someObject = [SomeClass alloc] init]; where someObject had previoulsy been initialized with an instance of SomeClass
Just for clarity when you say "What happens if I call [alloc] init] on an object..." your terminology is wrong.
The following line:
NSMutableArray* someArray = [[NSMutableArray alloc] init];
Reads in English:
"Send the alloc message to the NSMutableArray class object, then send the init message to the object returned from the first message, then store the object returned from init into the pointer variable named someArray."
I say that to emphasize the fact that you're not "calling alloc/init" on an existing object, you're making a new object, and storing a reference to this new object over the reference you had to the previous object. Since you no longer have a reference to that previous object, you've lost the ability to properly release its memory, so yes, you'll leak it.
correct, it will leak. Use NSMutableArray insertObject:atIndex:
There are a couple of ways that come to mind to do what I think you want. A sort of clumsy one is to put as many [NSNull null] objects into the array as you need so that it's filled up to the spot where you need to add the new object. Then you would replace an existing NSNull if you were storing your own object.
Probably a better approach is to use a dictionary instead of an array and turn your index value into a key.
Related
If I've created an NSArray in the init of an object.
Then later on I want to recreate a new NSArray to the same property should I set the old one to nil first?
i.e.
is it ok to just go...
self.arrayProperty = [[NSArray alloc] init];
or should I do...
self.arrayProperty = nil;
self.arrayProperty = [[NSArray alloc] init];
(I'm just using an array for the sake of this example but it's a general questions about properties).
If it makes any difference, it's a strong property.
The first approach is fine, you don't need to set it explicitly to nil before assigning a new object, since the setter releases the backing object of the property before retaining and assigning the new one. Just what you would do under MRC (except that here you don't autorelease the object).
It's just the same, as with ARC an object dies when there are no more references to it. The only difference is that in the second code you're doing a useless extra operation.
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.
Let's say I've got an array with strings.
NSArray *names = [NSArray arrayWithObjects: #"One", #"Two", #"Three", nil];
What I want is to initiate objects of some custom class and them add them to a mutable array. I'm using a custom init method that takes a string argument.
To be more specific, I want to [SomeClass alloc] initWithName: aName] and add the resulting object to a NSMutableArray.
I'm thinking of using Objective-C fast enumeration. So what I get is:
NSMutableArray *objects = [NSMutableArray arrayWithCapacity: [names count];
for (NSString *name in names) {
[objects addObject: [[[SomeClass alloc] initWithName: name] autorelease]];
}
The problem is that I can't add nil to the array and I don't like exception handling. However, my initiation method may return nil. So I decide to check first before adding (prevention). My new for-in-loop is:
SomeClass *someObject;
for (NSString *name in names) {
someObject = [[[SomeClass alloc] initWithName: name] autorelease];
if (someObject) {
[objects addObject: someObject];
}
}
Now, instead of immediately passing the new object to the array, I'm setting up a pointer someObject first and then passing the pointer to the array instead.
This example raises a question to me. When I someObject = [[[SomeClass alloc] initWithName: name] autorelease] in the loop, do the existing objects (which are added using the same pointer) in the array change too?
To put it in other words: does the addObject: (id)someObject method make a new internal copy of the pointer I pass or do I have to create a copy of the pointer — I don't know how — and pass the copy myself?
Thanks a lot! :-)
It's fine to reuse someObject; if you think about it, you're already reusing name each time you go through the loop.
-addObject: may or may not copy the object that you pass in. (It doesn't -- it retains the object rather than copying it, but it's conceivable that some NSMutableArray subclass could copy instead.) The important thing is that this code really shouldn't care about what -addObject: does.
Also, don't lose sight of the distinction between a pointer and the object that it points to. Pointers are just references, and a pointer is copied each time you pass it into a method or function. (Like C, Objective-C passes parameters by value, so passing a pointer into a method results in putting the value of the pointer on the stack.) The object itself isn't copied, however.
Short answer: no, you don't have to worry about reusing someObject.
Slightly longer answer: the assignment—someObject = ... assigns a new pointer value to the someObject variable; addObject: is then getting that value, not the address of someObject itself.
I think you're getting confused in the concept of pointer here. When you say someObject = [[[SomeClass alloc] init... you are basically pointing the someObject pointer to a new object. So to answer your question- your current code is fine.
As for whether arrays maintain copies of the objects added to them - NO, the array retains the object you add to it. However, that doesn't matter to your code above.
Three20 provides the answer!
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.