So I have an NSDictionary that contains several objects with properties (key:values), but the issue is that I need them in a specific order.
Stored as an NSArray is a list of objects that I would like to use as keys for the NSDictionary.
I can output the NSArray and NSDictionary to the Console, however when I to access each of the values keyed to "filename" with:
for(NSUInteger i=0; i<[images count]; i++)
NSLog(#"%#", [[dictionary objectForKey:[images objectAtIndex:i]] objectForKey:#"filename"]);
I get all nulls.
Anyone have any idea? I'm assuming it's because my [images objectAtIndex:i] returns something other than a string, however I've tried slappin in a (NSString *) and I still get nulls.
If the things in the dictionary aren't dictionaries, shouldn't that second objectForKey be valueForKey?
NSLog(#"%#", [[dictionary objectForKey:[images objectAtIndex:i]] objectForKey:#"filename"]);
I get all nulls.
Getting the key from the array is working fine. Either dictionary does not have an object for the key you retrieved, or the dictionary you retrieved does not have an object for the key #"filename".
I'm assuming it's because my [images objectAtIndex:i] returns something other than a string, however I've tried slappin in a (NSString *) and I still get nulls.
First, NSStrings are not the only valid keys. Any copyable object is usable as a dictionary key.
More importantly, remember that you never work directly with object structures, but with pointers to objects. objectAtIndex: returns a pointer to an object; when you cast its return value, you're telling the compiler that objectAtIndex: will return a pointer to an NSString object. Casting a pointer does not change the pointer itself; it only tells the compiler what to expect at the other end of that pointer. If objectAtIndex: is returning pointers to, say, Foo objects, casting them to pointers to NSString objects will not make them point to NSString objects; they will still point to Foo objects, so looking those objects up in a dictionary whose keys are strings will still fail.
The other thing to consider is: Why don't you know what is in this array? You created the array. You populated the array. Surely, then, you should know what's in it. objectAtIndex: does not return anything but (a pointer to) an object that is in the array.
If you think objectAtIndex: is returning objects that are not valid keys, then you should examine the array using either NSLog or the debugger. Check what the array contains and what those objects' classes are.
More probably, the array does contain objects that would be valid as keys, but are not keys in the dictionary, or it does contain objects that are keys in the dictionary, but the dictionaries that are the objects for those keys do not contain the #"filename" key. Examine both the array and the outer dictionary.
One other caveat, which may be what's tripping you up: If you're keeping mutable strings in the array, and those are the keys you initially inserted dictionaries into the outer dictionary under, and you mutate these strings, that makes them not match the keys in the outer dictionary. Remember that dictionaries copy their keys (this is why keys need to be copyable); you mutate the string you have in the array, but the dictionary made a copy previously, and that copy does not change. (Such is the point of making a copy: Whoever makes the copy—in this case, the dictionary—wants to keep the object as it is, without receiving any mutations that occur later on.)
So, if you want to revise any of the strings, you'll have to replace them in the array and store the outer dictionary's object for the old key under the new key. You'll also have to take steps to prevent duplicate keys; the dictionary only holds one object per key, while the array can contain the same object any number of times.
Related
I've got a bizarre question that I've been researching/playing with for hours now.
I'm working on a simple note-taking app, storing notes in an NSMutableDictionary with the Key as the title and the Value as the note. I've got a delegate function that saves the contents of an NSTextView each time it changes.
-(void)textDidChange:(NSNotification *)notification {
NSString *currentNote = [_mainTextField string];
[Data setNote:currentNote forKey:currentKey];
}
and then adds it to the Dictionary.
+(void)setNote:(NSString *)note forKey:(NSString *)key {
[allNotes setObject:note forKey:key];
}
while currentKey is a global variable that is updated elsewhere (and which I have tested thoroughly and is working fine).
Here's where it gets weird: You'd expect the Dictionary to update a single Key for each call of setNote:forKey: as long as the Keys are all unique (which they are). But instead, it's updating all previously-updated Keys each time. So if you had a Dictionary like this.
"John" = "apples";
"David" = "apples";
and you updated "John" to "bananas"
"John" = "bananas";
"David" = "apples";
and then you updated "David" to "oranges", you'd get this:
"John" = "oranges";
"David" = "oranges";
I've breakpoint'd and NSLog'd the hell out of it, and what I've discovered is that "John" changes to "oranges" by the beginning of the setNote:forKey: method, before the [allNotes setObject:note forKey:key] line is even called. Now, that's the only place in my entire program that changes the allNotes Dictionary, but somehow the original row is being re-changed before the function is even called. The first time you make a change (the "bananas" round) everything works perfectly regardless of the size of the Dictionary or which entry is being changed, and the second time (the "oranges" round) it all works perfectly again, except for changing the previously-changed rows as well.
I don't know how to get any more exact with the cause in my code, since the exact instant a key is pressed, textDidChange: gets called and by that point the first entry has already been altered. And notably, when I stop the app and re-run, it goes back to changing the first one normally, and begins the whole thing again.
The other weird part is, the Key variables are working fine. I've checked in the debugger: both the global variable currentKey and the local variable key derived from it will be "David", and yet the changes will affect both "David" and "John", and any others changed before "John" as well. How could it even know what lines were previously edited, with only the current Key value to work with?
I am admittedly a Cocoa beginner, but I am absolutely stumped. Can anyone shed any light on this for me?
When a key/value pair is added to a dictionary the key is copied while the value is retained. Keys are copied so they cannot be changed - the internal organisation of the dictionary is based on the keys.
The values are retained as they must continue to exist while in the dictionary, but if the value is a mutable type then it is OK for it to be mutated. E.g. If you create a dictionary of NSTextField objects then the association between keys and NSTextField objects is fixed, but the contents of the individual text fields themselves can change.
It looks like that in your code [_mainTextField string] is returning a reference to the same NSMutableString object each time it is called. This would mean that every value in your dictionary is a reference to the same mutable object and give you the behaviour you are seeing. You can address this by either changing [_mainTextField string] to return an NSString by copying the mutable string it is using internally; or by copying what is returned using [[_mainTextField string] copy]. The former is better if the purpose of [_mainTextField string] is to return a snapshot of the current value of _mainTextField (whatever its type is - the sample does not say).
Thanks To all that helped problem solved. :)
For some reason this wont work for me please help
array = [[NSMutableArray alloc] init];
inputted = [input.text doubleValue];
[array addObject:[NSNumber numberWithDouble:inputted]];
NSLog(#"%i",array.count);
where array is a NSMutableArray, inputted is a double and input is a text field
All that happens is that one saves but deletes the last one entered. how do i make it so that it saves everything entered?
You're always re-creating and re-initializing the "array" mutable array each time you go through your function so it's no wonder you are getting a result of "1" (one object in the array).
If you initialize your array once and only once, and move it out and away from the rest of that code (i.e. into a different function or whatever), then you will add additional objects to your mutable array and you'll see the count increment each time you add an object to your mutable array.
Makes sense?
I have two NSArrays of NSRects (stored using NSStringFromRect(NSRect)). Is there a quick way to check and see if the items in the array are equal or will I have to do a loop? So item 1 in array 1 = item 1 in array 2, etc. etc.
Thanks
If you check the NSArray Reference, you'll find a handy -isEqualToArray: method that should do just what you want
From the documentation for -[NSArray isEqualToArray:]:
Compares the receiving array to another array. Two arrays have equal contents if they each hold the same number of objects and objects at a given index in each array satisfy the isEqual: test.
This is exactly what you are looking for.
I have converted CGPoint To NSValue and put all the resulting values in q NSArray but when I use [NSKeyedArchiver archiveRootObject:ArrayName toFile:PointArrayFile]; it gives me error.
So what's the problem?
You don't say what problem you're seeing but in general you're probably better off using NSStringFromCGPoint and CFPointFromString to store and retrieve the points.
What is the best way to count the number of entries in a property list?
I currently build a dictionary from the plist entries (*) and then use the dictionary's count:
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:myPlistPath];
NSDictionary *myPlistDict = (NSDictionary *) [NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
NSLog(#"There are %d entries in the plist.", [myPlistDict count]);
This strikes me as unnecessarily "heavy", but I was not able to find a more efficient solution. Any ideas?
(*) targeting 10.5 and therefore using the deprecated +propertyListFromData:… class method.
Well... if you're converting to XML anyway, you could use NSXMLNode's childCount method. The documentation does suggest that it's more efficient than calling [children count], but the creation of the NSXMLNode might make this just as bad (or even worse than) the NSDictionary method.
Have you profiled? Are you working with particularly large plists? Are you requesting this count often? I say: use NSDictionary, cache the value if you request it often, and move on unless this is unacceptably slow. (Yeah, it looks ugly right now, but there are bigger things to worry about.)