NSDictionary + ARC + copy vs reference - objective-c

These are probably are pretty simple YES|NO type questions.
I have some NSDictionaries containing other NSDictionaries. Let's say NSDictionary_A and NSDictionary_B. These persist for the life of the app.
The NSDictionaries contained in NSDictionary_A are passed by reference to various objects:
track.instrument = [NSDictionary_A objectForKey:#"Blue"];
Later it gets changed:
track.instrument = [NSDictionary_A objectForKey:#"Red"];
So first question: The #property instrument is synthesized + retained as strong so does the setter for instrumentset the current value of instrument to nil before setting the new value, and if so, does this affect the source of the reference in NSDictionary_A - in other words, set the reference to nil'? Sounds wrong just writing it out.. so I think the answer is NO here. Also, it probably doesn't matter that the #property instrument is stored as weak or strong since the reference in NSDictionary_A1 persists for the app life but since it is a pointer, should be weak - YES?
Second question: An NSDictionary in NSDictionary_B is passed to an object but it can change some of the values in that NSDictionary:
track.playbackType = [NSDictionary_B objectForKey:#"Random"];
[track.playbackType objectForKey:#"maxRange"] = 20;
So should I be making a copy of the NSDictionary here because it's values will be changed or am I completely misunderstanding this whole reference passing thang?

You are getting mixed up in how pointers work.
For the first question, "track.instrument" is just a pointer. So it will start as "pointing to nil".
this:
track.instrument = [NSDictionary_A objectForKey:#"Blue"];
means, "stop pointing to nil and point to that object"
If you can ensure your dictionary will persist for the entire app then it doesnt matter, whatever is at #blue key will never get dealocated. But for the sake of having the correct code, it should be weak.
Edit: Had read the second question incorrectly.
Second question:
about this:
track.playbackType = [NSDictionary_B objectForKey:#"Random"];
first your pointer points to the NSDictionary from the dictionary.
[track.playbackType objectForKey:#"maxRange"] = 20;
Since it is a NSDictionary this is not valid. You cannot change NSDictionaries because they are immutable, it SHOULD be NSMutableDictionary.
HOWEVER if you are not interested in putting back the modified version into the original dictionary then you can copy it but as a NSMutableDictionary first, and then change it.
NSMutableDictionary *mutableDict = [[NSDictionary_B objectForKey:#"Random"] mutableCopy];
track.playbackType = mutableDict; //Note how track.playbackType has to be NSMutableDictionary aswell.
VERY IMPORTANT: Since you are creating a "new" dictionary. track.playbackType has to be strong, or it will simply get instantly dealocated after the function ends and mutableDict gets out of scope.

References are just pointers, setting one to nil will have no effect except in the following case: It is the last strong reference and other weak references still exist. In that case all the weak references will become nil. Strong properties will set the old value to nil, in effect sending a release call but this affects the REFERENCE, not the CONTENT of the reference.
As for the second question, it is quite confusing and I need more info about playbackType. You say it is an NSDictionary but NSDictionary doesn't have the property maxRange so it must be a type that you defined. You can't change the values of an NSDictionary either because it is immutable.
But here is a generic answer: If you pass a pointer to a mutable object as strong (or weak even) you will be able to change the content of the original. If you pass a pointer to a mutable object as a copy you will get a new object that doesn't affect the original.

Related

NSMutableDictionary and mutableCopy

I'm confused by the code, below. Before I added the mutableCopy line, it didn't work. After I added the line, it did.
Why isn't aDict mutable to begin with? I declared aDict as an NSMutableDictionary.
- (void) myRoutine: (NSMutableDictionary *) dictOfDicts
{
NSMutableDictionary * aDict = dictOfDicts[dictOfDictsKey];
int data = [aDict[aDictKey] intValue];
aDict = [aDict mutableCopy];
aDict[aDictKey] = #(++data);
}
The declaration of dictOfDicts says it's a pointer to a mutable dictionary. However, it does not use Objective-C generics syntax to say what the types of the keys or values are. So, the most we (and the compiler) can assume is that the keys are id<NSCopying> and the values are id, completely generic object pointers.
You then initialize your aDict variable with a value obtained from dictOfDicts. You've declared that aDict is also a pointer to a mutable dictionary. That's its "static type", but the real type of the object it points to is determined at runtime by whatever object is held in dictOfDicts under that key. It might be a mutable dictionary or it might be something else. It compiles just find because the compiler can't know what type of object that value is.
However, the real type (a.k.a. "dynamic type") of the object governs what operations succeed or fail/crash/whatever. In your case, it sounds like it's an immutable dictionary. So, when you attempt to mutate it, "it didn't work" (you don't specify what actually happened).
You make a mutable copy and you're allowed to mutate that. However, that's now a separate object that the one in dictOfDicts. So, you're not modifying what you think you are.
The solution is to put mutable dictionaries into dictOfDicts in the first place. Or, even better, put objects of a custom class of your own design into it, and operate on real properties.

Creating a NSMutableArray to hold pointers

I am trying to create a mutable array in objetive c to hold references to objects. The objects in the array are regularly updated through user interaction and i want the array to automatically reflect changes made to the objects as they occur. Does anyone know if there is a way to do this? Perhaps store pointers to the objects instead of the objects themselves in the array? Any help would be much appreciated
Thanks in advance
Edit: I should mention that the objects are not exactly being updated in the strict sense of the word. They are being reinitialized. For ex if i had a controller:
MyController = [MyController alloc] initWith.....]]
the above call is made again with different init parameters.
The array always stores the pointers.... It holds a strong reference to it or sends it a retain message (if using non ARC).
So
[myMutableArray addObject: anObject];
adds the pointer to it.
If you now change anObject's properties and access it later through the array, it will
give you the pointer to just that object with the changes to its properties.
Edit:
No, if you alloc/init, you are creating a new object instance and allocate new memory for it on the heap (ie, it's another pointer to a new memory address).
What exactly are you trying to accomplish? There sure is a way, if you provide a little more detail.
If you alloc/init the object with the same class, why not just create a method to change the object's properties:
Instead of
myObject = [[MyClass alloc] initWithParameter1: one parameter2: two];
You could create a method that changes these properties:
[myObject updateParameter1: anotherOne parameterTwo: anotherTwo];
And, of course, the advantage of a mutable array is, that you can change its contents, so like #Eli Gregory pointed out, you can replace an object with another one (or rather the pointers to it).
Because you want to point to a newly allocated and initialized object, you can't 'update' the pointer, what you can do is 'replace' the pointer with a new one at a certain index.
A method you could use to do this is:
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
so it would look something like:
NewViewController *new = [[NewViewController alloc] init..];
[myArray replaceObjectAtIndex:x withObject:new];

Assign or retain in cocos2d and objective C

Here's my current situation:
I have a NSMutableArray named dictKeyArray which I assign a property with #property(nonatomic,retain)NSMutableArray *dictKeyArray
I synthesize my mutable array in the implementation file.
Later, I have a dictionary name storeDict. I assign all the keys of the dictionary to the dictKeyArray like so:
dictKeyArray = [[storeDict allKeys] mutableCopy];
Now I use this dictionary later in my implementation file. However, when it comes to releasing it, I release it once in my dealloc method. When checking with instruments, a leak shows up! Why is dictKeyArray leaking? Should I be using assign instead of retain?
I'm still not clear on what the difference is exactly...
thank you!
You have to send it an
[[[storeDict allKeys] mutableCopy] autorelease];
Just to make this clear: mutableCopy does the same as alloc meaning you are claiming ownership of the object in question. You have to decrease the retainCount by one.
By the way: You should use the accessor you wrote for it. You are just assigning it to your iVar at the moment. If you want to make your accessors work, you will have to use something like
object.dictKeyArray = ...;
in general. Or here (as mentioned by dreamlax)
self.dictKeyArray = ...;
because you are referring to an object of this specific class the code is in.
Only this way you are ensuring your object is properly retained by your accessor. Otherwise writing the accessor code doesn't make sense at all because it never gets called.
Please note: As Josh said in the comments, your code should be valid (at least from my point of view). What I suggested is a solution that is not as error-prone as yours because you adhere to the rules (could save you from headache in the near future).
You should be using self.dictKeyArray = .... Without the self. you are accessing the instance variable directly, bypassing any memory management benefits of properties, but, remember that you own the result of mutableCopy, and assigning to a property that also takes ownership will result in double-ownership, so use:
self.dictKeyArray = [[[storeDict allKeys] mutableCopy] autorelease];

Use of an intermediary to initialize an object

I've always seen that we use an intermediary object, for example, creating an array to fill in another array:
characters = [[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil];
play.characters = characters;
[characters release];
with characters being an NSArray in the object play.
I saw the same thing with a #property and its self: we did not add the new items directly into this property, just as we don't directly fill in characters in the example above. Is this only about "style"?
This is not a matter of style.
play.characters is a property, and that can "contain" an existing array or nil, but even if it "contains" an existing array, you can't change the contents of an NSArray, so you'll have to create a new one and assign that to the property.
Assigning to a property will, if all was declared well, cause its setter method to run (which could be created by the compiler, if you used #synthesize, or written by you, in code) and that will take care of removing any existing array, assigning the new one and retaining it.
There is actually only one array in play in that little piece of code.
It is not the array that is intermediate, but the variable holding a pointer to it - in this case the variable characters.
This is what happens:
The expression
[[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil]
allocates an object and initialises it with three NSStrings (which are themselves objects, but let's leave that out for a moment). The initialisation also includes an increment of the retain count, so it is one from the get-go.
This newly created object lives at a given position in memory, say 0100H. This position is then stored in the variable characters. In C terms we say that characters is a pointer to the object.
Then the property #"characters" of the object play is set to point to the same position in memory as the local variable characters. There are therefore now two variables (of which one is also a property) that point to the same object, or, if you prefer, to the same position in memory. If the property is of type retain, this will automatically increment the retain count of the object, so it is now 2.
With the release message in the last line, the object decrements its retain count by one, so at the end of this code snippet, the object is pointed to by the play.characters property, and it has a retain count of one.
To be really clean, this code should probably set the local variable to nil, to avoid confusion between variables holding pointers to the object and the retain count.
All this was meant to show that there really is only one array in play here, but two variables that point to it. So there are not as many computer resources being wasted as it might seem at a first glance.
If you wanted to do it all in a single line, you could write something like this:
play.characters = [[[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil] autorelease];
but the exact working of this is less clear as it involves one of those mysterious autoreleases, i.e., a release that is handled automatically and postponed to some later stage.
This is a long description, but I hope it sheds some light on what is going on.

Releasing after removal from an array & reference pointers

So some where i have a leak which is related to deleting an object under certain circumstances.
Premise:
- I have an NSMutableArray of Tree objects (a Tree object knows how to draw itself).
- I have a reference pointer (Tree *selected) which basically points to whatever tree i last touched.
- Note that the *selected pointer is a weak reference.
Ok, so far so good.
Problem:
The leak arises when i delete a Tree. From the list i make sure the tree being deleted is releasing everything internally before removing it from the array (removing it from the array should automatically call release on it).
What i tried:
I noticed that my Tree *selected pointer is being assigned the touched tree via the self property:
self.selected = tree;
...and by doing this i know that it is being retained. So what i tried to do was call:
[self.selected release];
I called this right after the tree is removed from the array.
...but at which point it crashes essentially stating it was already released.
Question:
Why am i getting this error message? I removed it from the array, however my self.selected pointer still has a retained count, thus shouldn't i be releasing it?
Perhaps I should set it to nil after the removal process?
Or, perhaps I should set it to autorelease BEFORE the removal process?
Don't attempt to micromanage the retaining/releasing of your selected instance variable. If you want selected to be a weak reference, declare it with the assign attribute:
#property(nonatomic, assign) Tree *selected;
That way it will not be retained when you assign a value to it. Conceptually, it will "piggyback" on the reference that your NSMutableArray is keeping. So when you remove something from your NSMutableArray, do it a bit like this:
if (self.selected == toBeRemoved)
self.selected = nil;
[myArray removeObject:toBeRemoved];
If you didn't explicitly specify assign and your property was retaining its contents, you were most likely getting an exception because the next assignment to self.selected after removing an element caused another -release message to be sent to the old value, which had already been released by your [self.selected release] message.
What's [probably] happening is selected points to a deallocated object, not nothing.
I'd try
self.selected = nil;
instead of releasing it explicitly. That should take care of the retain count, too.
basically, i was assigning my weak references using 'self', thus internally calling retain. i removed the 'self' and now things are fine and dandy. :)
thanks!