Picking an item of NSArray using key value coding - objective-c

If you read listing 4 in the Animation section Apple's Core Animation guide, it seems to use KVC and the key path "filters.pulseFilter.inputIntensity" to pick out an object called "pulseFilter" out of an NSArray. "pulseFilter" is actually a CIFilter named "pulseFilter" by calling the method setName.
Now, I don't see the method setName defined anywhere. I also don't believe you can select a specific item out of an NSArray by using a key. Can someone explain how this works?

It does not necessarily have to go through standard valueForKey: or valueForKeyPath: of the NSArray.
Objects are free to provide own valueForKeyPath: method and handle KVC its own way and this is what probably the class of this selectionLayer object does.

To retrieve an object from NSArray using a property name + property value.
Step 1: get your stored object compliant to KVC (NSKeyValueCoding protocol) by implementing valueForKey: in your custom class.
Step 2: filter your NSArray using NSPredicate (filteredArrayUsingPredicate on your NSArray)
Do you need more detail ?

Related

Best way to add/remove to an NSMutableArray managed by an NSArrayController

I've got a NSMutableArray (containing NSMutableDictionary instances) bound to an NSArrayController (the NSArrayController is in turn bound to NSTableView columns).
What is the most Cocoa-, and KVO- friendly way of, programmatically :
adding a new empty object (NSMutableDictionary) to the array?
removing currently selected object? (after removing, the previous item - if exists - should be selected)
I've always been doing this in a way I don't particularly like - and I'm sure it's not the best way around (too many lines of code for something so simple : in Cocoa that indicates a wrong take on the subject :-)).
My code (quite an overkill, actually) :
Adding to the Array
NSMutableArray* oldParams = [paramsArray mutableCopy];
[oldParams addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:#"Parameter",#"Parameter",#"",#"Value", nil]];
[self setParamsArray:oldParams];
[paramsController setSelectionIndex:[paramsArray count]-1];
Removing currently selected object from the Array
if ([paramsArray count]>0)
{
int s = [paramsController selectionIndex];
NSMutableArray* oldParams = [paramsArray mutableCopy];
[oldParams removeObjectAtIndex:s];
[self setParamsArray:oldParams];
if (s<=[paramsArray count]-1)
[paramsController setSelectionIndex:s];
else
[paramsController setSelectionIndex:[paramsArray count]-1];
}
So, what are your opinions on that?
Given that the array controller is bound to a property named paramsArray on some object, the best approach is to define the key-value coding indexed accessors on that object's class. Then, use those accessors to mutate the to-many relationship represented by the property in a KVO-compliant manner.
For example, implement -insertObject:inParamsArrayAtIndex: and then use that to add an object. If you like the convenience of NSMutableArray's -addObject: method, you can write an -addObjectToParamArray method that forwards to -insertObject:inParamsArrayAtIndex:.
By the way, "paramsArray" is a poor name for a property. The property name shouldn't encode the type used to implement it. If you look at the templates for the indexed accessor names, you'll see that Apple is expecting to-many relationship properties to just be a plural noun like "params" (no "Array"). For example, -paramsAtIndexes: is better than -paramsArrayAtIndexes:.
You have to think of your array as the controller's backing store, and that it's managing it for you.
Adding an object:
[[self accountsArrayController] addObject:accountDictionary];
Removing the currently selected object:
[[self accountsArrayController] remove:nil];
You'll have to write another line or two to make that previous item selected, but that's an exercise I leave to you.

Object keys with NSMutableDictionary (Objective-C)

I want to store a bunch of key value pairs, with the key being my own object (ObjectA) that inherits from NSObject, and the value being an int.
I am trying to use an NSMutableDictionary. I understand that you can only store object types in the dictionary, so I have the following:
id value = [NSNumber numberWithInt:my_number];
[[self dictionary] setObject:value forKey:myObjectA];
Now that gives me an error, saying
-[ObjectA copyWithZone:]: unrecognized selector sent to instance
which is fine, I understand that object keys need to implement the NSCopying protocol. However I then read that you can do this by wrapping your objects using NSValue.
Can someone please explain how I would wrap my objects, and how I can then find the value by the key? Am I still able to use dictionary objectForKey:myObjectA or do I have to wrap myObjectA with an NSValue object while I'm searching as well? Or should I be implementing NSCopying on my custom class, or using a string key instead?
I am looking for this simplest and easiest way to use a dictionary, if I have to I'll implement a string key and use setValue:forKey: instead but I'd rather use the object key if I can.
Dictionary keys are always copied. So you simply need to implement the NSCopying protocol for your class, which is just the copyWithZone: method.
Additionally you should implement the isEqual: method for your class.
Edit: How to implement your copyWithZone: depends on a number of factors (main factor: deep vs. shallow copy). See Apple's Implementing Object Copy guide and this SO answer.
You could turn an id into an NSValue with:
NSValue* value = [NSValue valueWithNonretainedObject:object];
...
id object_ = [value nonretainedObjectValue];
but you need to manage the ownership outside of the dictionary. This is going to be a mess. It's better to adopt NSCopying.
There is also a 4th option: use a CFDictionary, which allows the object only can be CFRetain/CFReleased, not copied.
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks
);
...
CFDictionarySetValue(dict, myObjectA, value);
...
CFRelease(dict);
And if you're programming for Mac or iOS 6 and above, try NSMapTable.
NSMapTable* dict = [[NSMapTable mapTableWithStrongToStrongObjects] retain];
...
[dict setObject:#"?" forKey:foo];
...
[dict release];
In iOS 6 you can use NSMapTable (https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/NSMapTable_class/Reference/NSMapTable.html), which allows you to chose weak/strong attributes for the keys and objects.
You don't need to wrap your object using NSValue. What you have will work except you're missing a piece. For myObjectA's class you need to adopt the NSCopying protocol (see the docs for what to add). Once that's added the code you posted above should work correctly.
You might want to consider using strings though over your own object for the key. The key is required to be a string if key-value coding is going to be used to access it at all. So using a string will make life easier if you can take advantage of key-value coding anywhere you're using the dictionary.

How to test property existence and type based on NSString typed key?

In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.
For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:
Not all properties of the existingObj are present in the updateDict
Not all properties of the updateDict are available in the extistingObj.
Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
updateDict may contain values for keys that are uninitialized (nil) in existingObj.
This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:
// key is an NSString, e.g. #"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
[existingObj setValue:[updateDict objectForKey:key] forKey:key];
}
Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.
This makes for subquestion A: How does one test for a property setter, if you only have the property's name?
The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key #"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key #"color" that is #"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:
if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
// regular assignment
} else {
// perform custom assignment
}
Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.
Subquestion B: How does one test for the type of a property, if you only have the property's name?
I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.
P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.
If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,
NSString *key = #"displayName";
NSString *setterStr = [NSString stringWithFormat:#"set%#%#:",
[[key substringToIndex:1] capitalizedString],
[key substringFromIndex:1]];
if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
NSLog(#"found the setter!");
[obj setValue:someValue forKey:key];
}
Two remarks:
Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.
KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.
As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely #) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.
If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.

What is the difference between valueforKey:, objectForKey:, and valueForKeyPath:?

I have 2 questions:
What is the difference between valueForKey: and objectForKey:? Is it that one is for NSDictionarys (objectForKey:) and for others it is valueforKey:, or is it the reverse?
Also what is the difference between valueForKey: and valueForKeyPath:? Has it got something to do with Core Data?
Please help.
valueForKey: is part of the NSKeyValueCoding protocol and is therefore part of the key-value coding framework, which allows you to access class properties by name at runtime. That's how NIBs are loaded, for example — the names of properties associated with connections are loaded and then the values are set directly by name. This contrasts with the way that visual interface design tools often work in other languages, generating lots of hidden statically compiled code.
objectForKey: is defined on dictionaries only, and looks up an object by its key. An NSDictionary is a class that stores connections between values and keys.
So, valueForKey: could be used on an NSDictionary to return meta information about the dictionary, such as the count of objects inside it, the list of all keys, etc. objectForKey: would be used actually to look into the dictionary.
At runtime, the difference is that objectForKey: is a method with a completely opaque implementation. valueForKey: explicitly relies on subsequently calling named getters and setters. The reason for the latter is that you can extend key-value coding to key-value observing, where you ask to be informed every time a particular property on a particular object changes. At runtime that's achieved with a method swizzle, where the original setter is replaced by a new one that calls the previous setter and then sends out the required messages. Because all messages are dispatched dynamically, that's just achieved by modifying tables within the object instance.
So any object that is key-value coding compliant (which just means declaring and implementing your properties in the proper way, which the new-ish #property/#synthesize syntax does automatically) can be observed without the object itself having to implement any code.
There's further Apple stuff that uses key-value coding to achieve various things, including CoreData, but it's not specifically to enable any one other technology.
valueForKeyPath: is like valueForKey: except that it can traverse several objects. So you can have a root object with a bunch of properties, each of those properties is another object with another bunch of properties, etc, and using a key path you can retrieve a value way out at the leaf of that data structure rather than having to iterate through object after object for yourself.
In summary, valueForKey: and valueForKeyPath: provide information about object instances and interact with the dynamic nature of the Objective-C runtime. objectForKey: is a dictionary specific method that does dictionary tasks.
Additions:
An example, coded as I type and assuming that NSDictionary is key-value coding compliant:
NSDictionary *someDictionary;
// create someDictionary, populate it, for example (note: we assume photoOfKeys.jpg
// definitely exists, not a good idea for production code — if it doesn't we'll get
// a nil there and anything after it won't be added to the dictionary as it'll appear
// that we terminated the list):
someDictionary = #{ #"favouriteGarment": #"hat",
#"#allKeys" : [NSImage imageNamed:NSImageNameDotMac],
#(2) : NSArray.new };
NSObject *allKeys;
// we make no assumptions about which type #allKeys will be, but are going to assume
// we can NSLog it, so it needs to be a descendant of NSObject rather than 'id' so as
// to definitely respond to the 'description' message — actually this is just compile
// time semantics, but if someone else reads this code it'll make it obvious to them
// what we were thinking...
// some code to get all of the keys stored in the dictionary and print them out;
// should print an array containing the strings 'favouriteGarment', '#allKeys' and
// the number 2
allKeys = [someDictionary valueForKey:#"#allKeys"];
NSLog(#"%#", allKeys);
// some code to get the object named '#allKeys' from the dictionary; will print
// a description of the image created by loading photoOfKeys.jpg, above
allKeys = [someDictionary objectForKey:#"#allKeys"];
NSLog(#"%#", allKeys);
// `objectForKey is analogous to `objectForKeyedSubscript:`, aka
allKeys = someDictionary[#"#allKeys"];
allKeys is a property of NSDictionary as described here. I've also added a mapping from the NSString allKeys to a photograph of some keys. Whether I use the key-value coding valueForKey: methods or the NSDictionary objectForKey: lookup method dictates whether I read the property of the object instance or whether I send the object instance a message asking it to do its unique job.
objectForKey: is a method on NSDictionary for accessing the object associated with a key. valueForKey: is a method on NSObject for accessing any value associated with any object, through the name of a accessor method, property, and/or instance variable.
valueForKeyPath: can be seen as a shorthand for several calls to valueForKey:. You can think of it as sort of a xpath, if you will.
These two statements will result in the same output:
// Using nested valueForKey:
NSLog(#"%#", [[myObject valueForKey:#"foo"] valueForKey:#"bar"]);
// Can be done with a single valueForKeyPath;
NSLog(#"%#", [myObject valueForKeyPath:#"foo.bar"]);
valueForKey:and valueForKeyPath: are part of KVC (Key Value Coding). Introduction and in-depth documentation can be found here: http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/
valueForKey: and valueAtKeyPath: are methods defined in the NSKeyValueCoding informal protocol, and default implementations for both are provided by the root class NSObject.
objectForKey: is a method on NSDictionary.
valueForKey: takes a key to a property, while valueAtKeyPath: takes a so-called keypath. A keypath is a period-delimeted path to a specific property, like #"relationship.property".

Detect changes in NSArray in ObjC

I need to detect change in NSArray object - that is if some object was added/removed to/from NSArray or was just edited in-place. Are there some integrated NSArray hash functions for this task - or I need to write my own hashing function for NSArray ? Maybe someone has different solution ? Any ideas ?
All objects have a -hash method but not all objects have a good implementation.
NSArray's documentation doesn't define it's result, but testing reveals it returns the length of the array - not very useful:
NSLog(#"%lu", #[#"foo"].hash); // output: 1
NSLog(#"%lu", #[#"foo", #"bar"].hash); // output: 2
NSLog(#"%lu", #[#"hello", #"world"].hash); // output: 2
If performance isn't critical, and if the array contains <NSCoding> objects then you can simply serialise the array to NSData which has a good -hash implementation:
[NSArchiver archivedDataWithRootObject:#[#"foo"]].hash // 194519622
[NSArchiver archivedDataWithRootObject:#[#"foo", #"bar"]].hash // 123459814
[NSArchiver archivedDataWithRootObject:#[#"hello", #"world"]].hash // 215474591
For better performance there should be an answer somewhere explaining how to write your own -hash method. Basically call -hash on every object in the array (assuming the array contains objects that can be hashed reliably) and combine each together mixed in with some simple randomising math.
You could use an NSArrayController, which is Key-Value-Observing compliant. Unfortunately NSArray is only KVC compliant. This way you can easily monitor the array controller's arrangedObjects property. This should solve your problem.
Also, see this question: Key-Value-Observing a to-many relationship in Cocoa