I'm writing an app in XCode 3.2.3 for iOS 4. In my code, I am creating an NSArray of NSDictionaries, each containing two key/value pairs, a string and an NSArray of NSDictionaries (kind of confusing, I know). Just to give you a visual, here is the structure:
<NSArray>
<NSDictionary>
<NSString/>
<NSArray>
<NSDictionary/>
<NSDictionary/>
...
</NSArray>
</NSDictionary>
...
</NSArray>
What's weird is that when I add a new NSDictionary to the innermost NSArray, it seems to add the same dictionary to the NSArray contained within each of the outer NSDictionaries. Here's how I'm adding the innermost NSDictionary to the innermost NSArray:
[[outerDict objectForKey:#"innerArray"] addObject:innerDict];
So, just to recap, the output I want is something like this:
<outerArray>
<outerDict1>
<string>
<innerArray1>
<innerDict1>
<innerDict2>
</innerArray1>
</outerDict1>
<outerDict2>
<string>
<innerArray2>
<innerDict3>
<innerDict4>
</innerArray2>
</outerDict2>
<outerArray>
But what I'm actually getting is this:
<outerArray>
<outerDict1>
<string>
<innerArray1>
<innerDict1>
<innerDict2>
<innerDict3>
<innerDict4>
</innerArray1>
</outerDict1>
<outerDict2>
<string>
<innerArray2>
<innerDict1>
<innerDict2>
<innerDict3>
<innerDict4>
</innerArray2>
</outerDict2>
<outerArray>
Notice that the inner NSDictionaries are added to every innerArray. For the life of me I can't figure out why!!
Any help would be very greatly appreciated, thank you!
-Matt
You need to post more code.
[[outerDict objectForKey:#"innerArray"] addObject:innerDict];
adds innerDict only to innerArray in outerDict, nothing else.
That said, you might have put the same innerArray twice into two different outerDict, like this:
NSMutableArray* innerArray=[NSMutableArray array];
[outerDict1 setObject:innerArray forKey:#"innerArray"];
[outerDict2 setObject:innerArray forKey:#"innerArray"];
This adds the same innerArray twice, not two independent arrays. Then, if you modify the innerArray in outerDict1, that will modify the innerArray in outerDict2 too, because these two are the same arrays.
Related
another couple of questions about plists and objective c for the iphone.
Both questions relate to my plist which can be seen in my last last question.
First thing is to do with searching, I know this is possible but what is the correct way to go about this? Should I pull all the searchable objects into an array and use this to build a table? Or is it possible to itereate through the plist and simply just show the matches? Or is there some other way I am missing here? As a quick example in the following I would want to bring back the two 'Jones' results:
<dict>
<key>A</key>
<array>
<string>A Jones</string>
<string>A King</string>
</array>
<key>T</key>
<array>
<string>T Jones</string>
<string>T King</string>
</array>
Secondly, is it possible to call up a random result from the plist, I'm pretty sure it is, but again what would be the correct way to go about this?
I will admit to finding the plist a bit of a pain as it seems to me like a bit of a rubbish form of xml. And I am still finding iterating through a plist dictionary pretty confusing to some degree. Still, any thoughts on these two questions would be greatly appreciated.
Thanks :)
It is obviously possible to iterate through a NSDictionary values using -(NSEnumarator *)objectEnumerator;, you can also retrieve all the values with -(NSArray *)allValues; and you could also have a look to -(NSSet *)keysOfEntriesPassingTest:(BOOL (^)(id key, id obj, BOOL *stop))predicate; which returns a NSSet containing the keys for the value have passed the test (from Mac OS X 10.6).
About the second question, I think there's no 'better' way. Here is how I would do that :
Get all the keys of the NSDictionary using -(NSArray *)allKeys;
Get a random number
Pick up a key in the array using -(id)objectAtIndex:(NSUInteger)anIndex;
Retrieve the corresponding object in the dictionary using : -(id)objectForKey:(id)aKey;
Then you got your object.
Hope this helps.
EDIT:
Here is a simple way to iterate over the values in the NSDictionary :
// assuming you already have a well initialized dictionary
// first create a container
NSMutableArray *selectedObjects = [[NSMutableArray alloc] init];
// then retrieve an enumerator for the dictionary
NSEnumerator *e = [theDictionary objectEnumerator];
id anObject;
// iterate...
while((anObject = [e nextObject]) != nil) {
// do what you want with the object
// in your case each object is an array
NSArray *theArray = (NSArray *)anObject;
// ...
// if you find any of them interesting put it in the first array
if([[theArray objectAtIndex:0] isEqualToString:#"Jones"]) {
[selectedObjects addObject:[theArray objectAtIndex:0]];
}
}
// here are the selected objects in selectedObjects.
// you won't forget to release selectedObjects when you don't need it anymore
OK, maybe I'm not seeing clear anymore and hope you can help.
I'm trying to select an Object from a NSMutableArray using:
if([car.seat isEqualToString:#"fancyOne"]){
fancyThings = [[NSMUtableArray]init];
[fancyThings addObjects: car];
}
Now I forgot to tell you I'm new at this Objective-C, so maybe I'm thinking the wrong way.
What I'm basically trying to do is to get an Object from one array by selecting a value of it's components.
This is the way to do it, I am however keep having trouble with my if-statement.
If I leave out the IF-statement it does fill my other NSMutableArray with the exact same object (thisCar) but if I put in the IF-statement it doesn't pick up that the string is the same in thisCar.seat.
I next example it puts everything in the normalThings but there are some aCar.seats which contain the string FANCYONE. I checked the XML file on spaces and that sort of things but everything is in order as far as I can see.
Shall I build it using NSScanner instead of IsEqualToString?
- (void)viewDidLoad {
appDelegate = (XMLAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.fancyThings = [[NSMutableArray alloc]init];
for (CARS *aCar in appDelegate.someCars) {
if ([aCar.seats isEqualToString:#"FANCYONE"]){
[appDelegate.fancyThings addObject:aCar];
}
else {
[appDelegate.normalThings addObject:aCar];
}
}
self.title = #"Cars";
super viewDidLoad];
}
EDIT:
My BAD!! The code supplied was in fact in order!
There was a mistake in my XMLParser, which added blank lines to the strings, so I couldn't get an equal string!
Hopefully this will give you some guidance:
//init new array
NSMutableArray *fancyThings = [[NSMutableArray alloc] init];
//walk your array
for (SomeCarObject *thisCar in arrayOfCars) {
//is thisCar a qualifying object
if ([thisCar.seat isEqualToString:#"fancyOne"]) {
//yes, add thisCar object
[fancyThings addObject:thisCar];
}
}
You'll want to create that NSMutableArray outside of the for loop (assuming you're iterating through a collection). Then you can add to that NSMutableArray like you did.
Hope this helps!
BTW, you should edit your question with the comment you made to elaborate on it..
It's depends from volume of objects, which u deal with. If there is 1000 objects or less, this method looks good. But if there is more objects, u have risk to freeze u application and have a big memory leaks.
Also if u will need concurrency code later, u have to keep in u mind some
other solutions.
U can using not just a string objects in u array, u can try to fill u array after application startup in objects, which response if string is same or not. Or using nsdictionary with appropriate keys.
Please read my post multithread search design
i'm in a bit of a situation here...
i am passing a string to a function and in that function i need to create an array whose name is the value of the string.
Say, for example: -(void) function : (NSString *) arrayName; //let arrayName = #"foo";
In this function I need to create an array named "foo" i.e the value of the passed parameter.
Can anyone help please :|
Thanks in advance ;)
Arrays don't have names. Variables have names, but variables are local to their scope, so once you leave the scope of that method, having a variable named "foo" is pointless; you can name the variable whatever you want and it will work just fine. Ex:
- (void) function:(id)whatever {
NSArray * myVariable = [NSArray arrayWithStuff....];
//use myVariable
}
What are you really trying to do?
That is not possible in Objective-C, but you can use e.g. a dictionary that maps a string to an array.
E.g. assuming something like the following property:
#property (readonly, retain) NSMutableDictionary *arrays;
... you can store an array by name:
- (void)function:(NSString *)arrayName {
NSArray *array = [NSArray arrayWithObjects:#"foo", #"bar", nil];
[self.arrays setObject:array forKey:arrayName];
}
... and access it like so:
NSArray *array = [self.arrays objectForKey:arrayName];
C is a compiled language where any source code names (for variables, functions, etc.) are not available at runtime (except for perhaps optionally debugging, -g). The Objective C runtime adds to this the ability to look up Obj C methods and classes by name, but not objects, nor any C stuff. So you're out of luck unless you build your own mini-language-interpreter structure for reference-by-name. Lots of ways to do this, but simple languages usually build some sort of variable table, something like a dictionary, array, or linked-list of objects (structs, tuples, etc.) containing string name, object pointer (maybe also type, size, etc.).
I'm attempting to create an NSArray with a grouping of string literals, however I get the compile error "Initializer element is not constant".
NSArray *currencies = [NSArray arrayWithObjects:#"Dollar", #"Euro", #"Pound", nil];
Could someone point out what I'm doing wrong, and possibly explain the error message?
New syntax for creating an array with string literals:
NSArray *currencies = #[#"Dollar", #"Euro", #"Pound"];
To fix your complication error the code must be in a method. If you want to use it statically then create a class method that follows the singleton pattern.
This isn't a problem with the NSArray creation itself (you would get the same error if you wrote [NSArray array] instead), but with where you've written it. I'm guessing this is a global or file-static NSArray. In C, that kind of variable has to have a constant initializer — meaning not a function call (or, by extension, a method call). The solution is to put the actual creation and assignment of the array into a method that will be called before you need the array, such as initialize.
It sounds like Chuck has spotted the problem. One thing you want to be aware of though in coding your solution is that you'll want to avoid storing an autoreleased instance of NSArray in a static variable. Also, a common pattern for these situations is to write a class method that creates and returns the value stored in the static variable, like so:
+ (NSArray *)currencies
{
static NSArray *_currencies;
// This will only be true the first time the method is called...
//
if (_currencies == nil)
{
_currencies = [[NSArray alloc] initWithObjects:#"Dollar", #"Euro", #"Pound", nil];
}
return _currencies;
}
Although this is old, please notice that Apple committed a new patch to the llvm project adding support for new Objective-C literal syntax for NSArray, NSDictionary and NSNumber.
See here and here
I'm a newbie in objective-c, but I think that the correct code is:
NSArray *currencies = [[NSArray alloc] initWithObjects:#"Dollar", #"Euro", #"Pound", nil];
I am not sure tho.
There's nothing wrong with that code. Are you sure the error is being produced at that line?
I have use below syntax for for set the object.
[dict setObject:eventArray forKey:categoryName];
Now i am trying to get below syntax but i got nothing.
NSMutableArray *tempArrayValue=[[NSMutableArray alloc]init];
tempArrayValue =[tempDict valueForKey:categoryValue];
What is the problem i cant understand can u help me?
you have given key as categoryName not categoryValue, and while retrieving you are using categoryValue.
NSMutableArray *tempArrayValue=[[NSMutableArray alloc]init]; tempArrayValue =[tempDict valueForKey:categoryName];
If you're setting the value like this:
[dict setObject:eventArray forKey:categoryName];
Then you should be fetching it back again like this:
NSMutableArray* eventArray = [dict valueForKey:categoryName];
assuming that eventArray is of type NSMutableArray.
What you are doing has at least two different problems.
This line is a memory leak, since you allocate an object and then throw it away, so delete it:
NSMutableArray *tempArrayValue=[[NSMutableArray alloc]init];
This second line may return a nil object, if there is no object stored for the key categoryValue. (You are using an object called categoryName above, as the key to store the value):
tempArrayValue =[tempDict valueForKey:categoryValue];
You haven't posted enough code to be able to tell why it's not working otherwise.