I dont know how to declare a array which just stores pointers to objects. As per My understanding ,if I use method
[ someArray addObject:someObject ] ,
It would then add the copy of object to array and any changes to object wont get reflected to original object.
What I want is that create a array of pointers which would just point to objects and changes made to objects would persist. pardon me, If I am missing something basic.
An NSArray or NSMutableArray is an array of pointers. No copying is done.
you have your basics wrong. technically when you do that you create the array of pointers to those objects.
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html read the description.
If you want to get the object copied you have to explicitly say so.
Look at this question for example
Deep copying an NSArray
By the way you should use an NSMutableArray.
Also look at the superclass NSArray
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html#//apple_ref/occ/cl/NSArray
specifically for the initWithArray:copyItems:
flag
If YES, each object in array receives a copyWithZone: message to
create a copy of the object—objects must conform to the NSCopying
protocol. In a managed memory environment, this is instead of the
retain message the object would otherwise receive. The object copy is
then added to the returned array.
If NO, then in a managed memory environment each object in array simply receives a retain message when it is added to the returned
array.
By default adding an object to a nsmutablearray increases its capacity if necessary, adds a retain for the object, and the pointer to the object.
...if I use method
[ someArray addObject:someObject ] ,
It would then add the copy of object to array and any changes to
object wont get reflected to original object.
While it technically doesn't pertain to the question, I simply must correct your terminology. "Copy" in Objective-C implies that the method -copy is sent to the object, which would create a new object in of itself. What Arrays do is send -retain to their objects, which means that the array itself now owns a stake in the object, which is why changes that don't reference the array (-objectAtIndex:), or have a valid claim to the object itself are not reflected.
What I want is that create a array of pointers which would just point
to objects and changes made to objects would persist. pardon me, If I
am missing something basic.
Well, unfortunately iOS does not support the class NSPointerArray, which would make your life significantly easy in regards to an actual array of pointers. Without getting into any C-craziness, I can only reiterate what I mentioned above: If you need to mutate an object in an array, just access it with a valid reference to it, or use -objectAtIndex. So long as you still have a valid claim on the object (a reference in this case, it's pointer didn't change because it was sent -retain) you can change it. Note the simple example below:
NSMutableString *str = [[NSMutableString alloc]initWithString:#"Hello"];
NSArray *arr = [[NSArray alloc]initWithObjects:str, nil];
NSLog(#"%#",arr);
[str appendString:#" Friend!"];
NSLog(#"%#",arr);
This prints:
2012-08-07 21:37:46.368 .MyApp[2325:303] (
Hello
)
2012-08-07 21:37:46.369 .MyApp[2325:303] (
"Hello Friend!"
)
Simple!
Related
the code below is working, but I want to make sure it's correct. I'm nervous about having an empty Array inside my dictionary that I create from the plist, since typically it seems that if you don't, say, initWithCapacity:1 then you often get memory errors once you start trying to add items.
At least, that's been my experience with NSMutableDictionary. However, this is the first time I'm trying to implement nested data objects, so perhaps the reason this code works is that the nested array is automatically initialized when it's imported as part of its parent dictionary?
Any and all comments appreciated. Thanks.
First, here's what the plist looks like that I'm using to create my dictionary:
Next, here's my code where I'm using the plist to create a dictionary, then adding an item to dataArray
// Create a pointer to a dictionary
NSMutableDictionary *dictionary;
// Read "SomeData.plist" from application bundle
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"SomeData.plist"];
dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:finalPath];
// Now let's see if we can successfully add an item to the end of this empty nested array. How 'bout the number 23
NSNumber *yetAnotherNumber = [NSNumber numberWithInt:23];
[[dictionary objectForKey:#"dataArray"] addObject:yetAnotherNumber];
// Dump the contents of the dictionary to the console
NSLog(#"%#", dictionary);
Okay, fine, simple, good. When I Log the dictionary contents it shows that "23" has been added as an array value to dataArray. So the code works. But again, I want to confirm that I'm not "getting lucky" here, with my code just happening to work even though I'm not properly initializing that nested array. If so, then I could run into unanticipated errors later on.
So to sum up, dataArray is an empty array inside the .plist, so do I need to initialize it somehow (using, for example initWithCapacity: or something else) before I can properly populate it, or is the way I'm coding here just fine?
Thanks again.
EDIT
Hey all. I've been doing continued research on this, in the interests of finding a satisfying answer. I think I may have stumbled upon something, via this link on deep copying. His previous posts on deep copying had presented some code to do essentially what I was looking for above: create a mutable copy of a dictionary or array, from a plist, that also has mutable sub-structures.
However, as mentioned in the link above, it looks like these methods were superfluous, due to the CFPropertyListCreateDeepCopy method, which can be invoked with a call such as
testData = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, [NSDictionary dictionaryWithContentsOfFile:path], kCFPropertyListMutableContainersAndLeaves);
So, my question is, can I properly use CFPropertyListCreateDeepCopy, in the way shown, to achieve what I've been asking about here? In other words, can I use this method to import my dictionary from a plist with fully mutable, nested data objects?
As I mentioned in one of the comments, I know I can create a nested, mutable dictionary manually, but for complex data that's just not practical, and it seems unlikely that built-in methods to import a mutable plist don't exist. So, based on the above, it looks like I've possibly found the solution, but I'm still too new to this to be able to say for sure. Please advise.
(Side note: I would simply test the code, but as we've established, the current SDK is buggy with regard to allow you to edit immutable nested dictionaries, contrary to the documented behavior. So as before, I'm not just interested in whether this works, but whether it's correct)
Thanks in advance.
init... methods should only be called once, immediately after a call to alloc or allocWithZone:. When framework code creates and returns an object or graph of objects, their init... methods have already been called, so sending another init... message would have undefined results. Don't do that.
Interestingly, in spite of what the documentation appears to say (and admittedly I probably missed a key sentence or paragraph somewhere), when you create an instance of a mutable collection by reading a plist, any nested collections are also mutable. I ran the following little experiment in a test harness just to be sure:
NSMutableDictionary *pets = [NSMutableDictionary dictionaryWithContentsOfFile:#"/tmp/Pets.plist"];
NSMutableArray *cats = [pets objectForKey:#"cats"];
[cats addObject:#"Foo"]; // EDIT: Added line I accidentally omitted earlier
NSLog(#"%#", cats);
So again, the nested collections created when you read in the plist are fully initialized, and mutable to boot, so you can simply use them, as you've been doing.
EDIT
However, after doing some further reading of the docs, I think the OP is right to feel uneasy about relying on what is apparently an undocumented feature of the current version of the SDK. For example, the Property List Programming Guide states:
If you load the property list with
this call:
NSMutableArray * ma = [NSMutableArray arrayWithContentsOfFile:xmlFile];
ma is a mutable array with immutable
dictionaries in each element. Each key
and each value in each dictionary are
immutable.
So, to be on the safe side, if you need a nested collection to be mutable, you should create it yourself. For example, I'd recommend rewriting the code in the example above as follows:
NSMutableDictionary *pets = [NSMutableDictionary dictionaryWithContentsOfFile:#"/tmp/Pets.plist"];
NSArray *cats = [pets objectForKey:#"cats"];
NSMutableArray *mutableCats = [cats mutableCopy];
[pets setObject:mutableCats forKey:cats];
[mutableCats release];
You can then safely make changes to the nested mutable collection:
[mutableCats addObject:#"Foo"];
Any object in a dictionary which is created by reading from disk will be properly initialized. You will not have to do it on your own. However, as pointed out by jlehr, contents of the dictionary should be immutable. If you want the contents of the dictionary to be mutable, you will need to change them on your own. I have no idea why your program is not throwing an exception.
I do not know why you are getting memory errors while not using initWithCapacity:1 in other situations. The following code is perfectly valid:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:#"object1"];
[array addObject:#"object2"];
NSLog(#"%#",array);
[array release];
If you don't specify a capacity, the array won't pre-allocate any memory, but it will allocate memory as required later.
Edit:
It is perfectly acceptable to use NSDictionary with CFPropertyListCreateDeepCopy. In Core Foundation, a CFPropertyList can be a CFDictionary, CFArray, CFNumber, CFString, or CFData. Since NSDictionary is toll-free bridged to CFDictionary, you can use it wherever a CFDictionary is asked for by casting, and vice-versa. Your code as is will give a warning, but you can suppress it by casting the dictionary and return values.
NSDictionary *testData = (NSDictionary*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)[NSDictionary dictionaryWithContentsOfFile:path], kCFPropertyListMutableContainersAndLeaves);
So, I'm fairly certain that if I plan on manipulating strings often, such as with stringByAppendingString, I should be using variables of type NSMutableString.
But what if I'm doing something like this?
UILabel *someLabel = [[UILabel alloc] init];
[someLabel setText: [[someDictionary objectForKey:#"some_key"] stringByAppendingString:#"some other string"];
I read that if you use stringByAppendingString on an NSString, you end up with leaks because the pointer associated with the initial NSString moves around, pointing to the new string created by the append, whereas with NSMutableString, your pointer always points to that mutable string.
So my question is, what is implicitly happening when I call stringByAppendingString on something that is a string, but not explicitly an NSString or an NSMutableString? Such as, in my above case, the value of some key in a dictionary. Is doing this wrong, and should I be doing something like below?
[[[NSMutableString stringWithString:[someDictionary objectForKey:#"some_key"]] stringByAppendingString:#"some other string"]]
I read that if you use
stringByAppendingString on an
NSString, you end up with leaks
because the pointer associated with
the initial NSString moves around,
pointing to the new string created by
the append, whereas with
NSMutableString, your pointer always
points to that mutable string.
That sounds like the advice of someone who didn't quite have a grasp of what is going on with the memory management. Sure, [NSString stringByAppendingString] returns a new string. But what you do with that new string is up to you. You could certainly cause a memory leak by reassigning the result to a retained property in a careless fashion, like so:
myStringProperty = [myStringProperty stringByAppendingString:#" more bits"];
The correct form would be to use self, like so:
self.myStringProperty = [myStringProperty stringByAppendingString:#" more bits"];
Follow the cocoa memory guidelines.
As for dictionaries and other collection types: treat what comes out of the dictionary appropriately given the type you know it to be. If you pull an object out which is actually an NSString, but try to use it as a NSMutableString, your app will fall over (with 'selector not found' or similar). So in that case, you do need to make a new NSMutableString from the NSString.
Interesting note: Apple chose to make NSMutableString a subclass of NSString. Something about that seems unwise to me -- if something looks to be immutable, because it has type NSString, I want it to be immutable! (But in fact it could be NSMutableString.) Compare that to Java, which has a String class and a completely separate BufferedString class.
I've always been a fan of [NSString stringWithFormat#"%#%#", a, b]; because then you clearly get a new autoreleased string and can dispose of "a" and "b" correctly.
With [someDictionary objectForKey:#"some_key"], you will be getting the type of object that was put into that dictionary originally. So blindly calling stringByAppendingString without knowledge of what's in that dictionary seems like a bad idea.
-stringByAppendingString is going to return you a new NSString that is distinct from both strings involved. In other words:
NSString *string3 = [string1 stringByAppendingString:string2];
string3 is an entirely new string. string1 isn't changed at all, nothing happens to its memory location or contents. The person who told you that probably just misunderstood what was going on.
[mutableString1 appendString:string2];
In this case, mutableString1 still points at the same object, but the contents of that object have been altered to include string2.
One last thing to keep in mind is that if you are using mutable strings, you should be careful with sharing references to it. If you pass your mutable string to some function which keeps a pointer to that mutable string and then your code changes that mutable string at some point in the future, the other reference is pointing at exactly the same object which means the other code will see the change as well. If that's what you want, great, but if not you must be careful.
One way to help avoid this problem is to declare your #property statements for NSStrings to be "copy" instead of "retain". That will make a copy of your mutable string before setting it in your property and the -copy method implicitly gives you a NON-mutable version, so it'll create an NSString copy of your NSMutableString.
If you follow the rules for memory management, you will be fine using stringByAppendingString. In a nutshell:
if you own an object, you need to release or autorelease it at some point.
you own an object if you use an alloc, new, or copy method to create it, or if you retain it.
Make sure you read up on Apple's Memory Management Rules.
In the first code sample in your question, you aren't using alloc, new, copy or retain on any of the NSStrings involved, so you don't need to do anything to release it. If outside of the code that you've included in the sample you are using alloc, new, copy or retain on any NSStrings, you would need to ensure that they are released later.
I have a mutable array that has been retained.
This array contain dictionaries with lots of keys. Each dictionary contains objects.
Do I have to retain the dictionaries before adding them to the mutable array or will the array itself retain everything that is added to it (because it is already retained), including the sub objects of its objects in the hierarchy?
thanks.
A Foundation container, like NSArray or NSDictionary, retains the objects it directly owns, but not subobjects owned by the objects.
For example, if NSArray*a contains NSArray*b and it in turn contains NSArray*c, a retains b and b retains c but a doesn't retain c.
That said, your are thinking from a wrong perspective. It's not correct for you to wonder such as "do I have to retain this object (say x) before passing x to another object y, because y might not retain it appropriately?" The point of retain/release is that to make sure an object retains and releases objects it owns. You trust other objects to do the same.
Then, all you have to make sure if you put an object x to an array y, is for you not to release x (if it's not autoreleased) once it becomes unnecessary to you. If y needs it, y retains it, so you don't have to care about it.
Say you have a pre-existing NSMutableArray*array. Then you would do in a method something like this:
NSMutableDictionary* dictionary=[[NSMutableDictionary alloc] init];
... do something with dictionary ...
[array addObject:dictionary];
[dictionary release];
You see, it's the array's responsibility to retain the dictionary, if that array needs it. It needs it, and so it retains it. You don't have to care about that.
The method's responsibility is to retain the dictionary if the method needs it, to release it if the method no longer needs it. So, as shown above, the method releases it once it's done with it by adding it to the array.
Again: the whole point of retain/release is to allow you to consider the life cycle of an object very locally in the code.
Whenever you call a method method:of another object a by passing an object b, you don't have to worry as you do now whether method: retains b or not, and you don't have to worry if you need to retain b before passing b to method:.
It is because every method in the Cocoa framework, and every method you write, retain the object b passed to it if the method needs it later, and don't retain b if it doesn't need it later.
Objective-C containers (such as NSMutableArray) will retain the objects added to them.
This does not, however, have anything to do with anything being "propagated" -- whether or not you call -retain on the NSMutableArray is irrelevant. The NSMutableArray will simply retain objects added to it, and if those objects are themselves some kind of container (such as a dictionary), the sub-objects will themselves already have retained anything added to them, and so forth.
ps. there isn't really a "retain property", there's an (internal) "retain count" on each object. For example, if you create an NSString and add it to 3 NSMutableArray's, each of those arrays will retain it.
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 am implementing a class that has to store arbitrary objects together with a string. i.e.
myUIViewObject, #"that's a nice view"
myUIViewController, #"not really special"
myOtherObject, #"very important one"
this list can be extended and modified at any time, so I thought about using NSMutableDictionary here. But I am not really sure...
The object should be the key, i.e. I want to find easily the matching string for myUIViewController or myOtherObject when I ask for it like so:
- (NSString*)checkObjNoteStringForObject:(id)anyObjectInList;
The other problem is, that when an object gets added to that "list", I don't want it to be retained because of that. NSMutableDictionary retains it's contents, right? Could I just send a -release afterwards to undo this unwanted behaviour, and when removing from the list just sending -retain before doing so? Or is there a more elegant way?
What do you suggest? Thanks # all!
If your dictionary key is not retained, once it is deallocated accesses to the dictionary will lead to undefined behaviour (in practice, they'll crash if a lookup happens to hit that dictionary element). To do what you want, you need a strategy to remove the objects from the dictionary when necessary.
If you do have one – for instance, overriding the objects’ -dealloc and removing them from there – you can do what you want using +[NSValue valueWithNonretainedObject:]. The NSValue will refer to your object without retaining it, and the dictionary will copy the NSValue (keys are copied, not retained). Just remember to create an NSValue for each time you want to look something up in the dictionary; a helper function or method is a good idea.