I am trying to observe changes to a transient attribute in my entity that is dependent on other attributes in the same entity. I want to do this by implementing keyPathsForValuesAffectingValueForKey:. The problem is that this doesn't seem to be invoked for all the attributes in the entity.
My entity has 10 attributes and about 5-6 relationships, and the keyPathsForValuesAffectingValueForKey is called 5 or 6 times with some mix of attributes and relationships, but not all of them, including the transient attribute that I'm really interested in.
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:#"todoSectionTitle"])
{
NSSet *affectingKeys = [NSSet setWithObjects:#"todoStatus", #"todoStartDate", #"timeNow", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys];
}
return keyPaths;
}
In this case, keyPathsForValuesAffectingValueForKey is called multiple times, but the key is never "todoSectionTitle" (a transient attribute). Neither is the key ever equal to "todoStatus" though that is a non-transient attribute. The key IS equal to "todoStartDate" in one of the times it is called. It can also be equal
What is the logic behind when keyPathsForValuesAffectingValueForKey: and which keys it is called for, and which keys it ignores? The docs don't seem to shed any light on this.
+keyPathsForValuesAffectingValueForKey: is part of Key-Value Observing (KVO). KVO only needs to call it if something observes a key on an instance of the class which implements it. (That includes any keys observed indirectly. For example, if something observes key "A", it will call the method for key "A". If it indicates that the value of "A" is affected by key "B", then it will also call it for key "B" to see if some other key(s) affect the value of "B".)
If nothing is observing todoSectionTitle or any key whose value is affected by todoSectionTitle, then KVO has no reason to call +keyPathsForValuesAffectingValueForKey: with "todoSectionTitle". In fact, KVO no way to know that there is such a key.
Bindings are built on top of KVO, so all of this applies to them, too.
Related
In a previous question I asked how I could show the contents of a Dictionary in a GUI. I started from this idea to build a GUI with a slightly better look and feel. It mainly consists of RectangleMorphs glued together in columns and rows (cfr. the accepted answer in my previous question).
The problem now is that I would like my table to be updated when elements are added/removed/edited in my dictionary. I managed to write some Morph that consists of columns of CellMorphs, which inherit from RectangleMorph and have model and message as instance variables with the following update message:
update
" update the contents of this cell "
| value |
self removeAllMorphs.
(model = nil or: message = nil)
ifTrue: [ value := '' ]
ifFalse: [ value := model perform: message ].
self addMorph: value asMorph.
As can be seen, the CellMorph is a container for a Morph containing the actual content of the cell. This works great for displaying the size of the dictionary for instance:
d := Dictionary new.
d at: 'foo' put: 100.
d at: 'bar' put: 200.
cell := CellMorph new
model: d;
message: #size;
color: Color white.
cell openInWorld.
d at: 'boo' put: 300. " cell will be updated "
but I don't seem to get something similar working for the contents of the dictionary, because I can't find a way to access single keys or values with a message. The only solution I can think of is to create new columns with new cells every time, but this is so expensive and I can't imagine that this is a good idea...
Therefore my question:
Is there a way to update my Morph displaying the dictionary without creating billions of my CellMorphs or should I forget about my idea and rather work with rows of CellMorphs for instance in order to group the entries in the dictionary?
for completeness: the model: message in CellMorph looks like:
model: newModel
"change the model behind this cell"
model ifNotNil: [ model removeDependent: self ].
newModel ifNotNil: [newModel addDependent: self].
model := newModel.
self update.
update: aParameter does nothing more than call update. and I also added self changed. in all messages of Dictionary that I want the interface to be notified of (at: put:, removeKey:, etc.).
In the instance variable named 'message' you could have a Message object, instead of having only the selector.
An instance of Message has the receiver, selector and arguments. So, you could configure it with the dictionary keys sorted asArray in the receiver, the selector #at: and an index, to get a specific key. Accessing the value would be getting the value at: that key in the dictionary.
I think that a Message is not executed with object perform: message, you should check. message perform should work because it already has the receiver.
In any case, this complexity may show that having only (one) model and (one) message is not enough to get the model in th granularity you want, and you can possibly specialize a bit more, using the knowledge that the model is a dictionary. For instance, having an instance variable for key or for keyIndex.
Some side notes about the code:
(model = nil or: message = nil)
has comparisons with nil, that can be replaced by #isNil message or, if you want to stick with equality, use the faster == to compare identity, since nil is unique.
#or: is used to get the benefits of partial evaluation (the argument is evaluated only if the receiver is false). But that only works if you have a block as argument, otherwise the expression is evaluated before, to get the argument for the message in the stack.
I have the following NSMutableArray:
(A|B|C|D|E|255,
F|G|H|I|J|122,
K|L|M|N|O|555)
I am trying to sort the objects in the array using the last component (255, 122, 555). Right now I have the following code:
[myArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
As expected, this method sorts the array by the first element (A, F, K).
I also read about NSSortDescriptor, for example:
NSSortDescriptor *sort = [[[NSSortDescriptor alloc] initWithKey:#"dateModified" ascending:YES] autorelease];
If I use it, it is not clear what I put as a parameter in initWithKey.
You can use a sort descriptor, which takes the last object of the "inner" arrays and sort by that.
Since sort descriptors use key-value coding (KVC), you need to be aware that arrays respond to valueForKey: in a special way - they pass a normal key on to each of the objects that they contain.
You also need to know that methods which do not take a parameter and return a value can be accessed through KVC is if they were normal properties.
All this adds up to the following:
Each of the objects contained in your array (i.e., the inner arrays) have a key that you want to sort by: lastObject
But since the objects are instances of NSArray they will normally pass the key on to the objects that they contain - which is not what you want.
You therefore need to use a special escape in the key name for that situation, which is #, making the actual key to use #lastObject
So to make a long story short, you can do what you want in this way:
NSMutableArray *array = ... // Your array
NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey: #"#lastObject"
ascending: YES
selector: #selector(localizedCaseInsensitiveCompare:)];
[array sortUsingDescriptors: #[sd]];
You'll notice the "#" in the key name, within the string.
This escape character also works for other collection classes, for instance if you want to access allKeys from a dictionary through KVC, the key you should use is #allKeys.
I would use sortUsingComparator:
[myArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2){
return [obj1.lastPropertyForComparison compare:obj2.lastPropertyForComparison];
}
This method allows you to manually compare the properties or members of the objects that you want to order by. I use it almost exclusively for complex sorts, and I haven't noticed any performance differences.
UPDATE:
If your NSMutableArray contains NSArrays with the last object being the number you're trying to rank by, your comparator would be as follows:
[(NSNumber *)[obj1 lastObject] compare:(NSNumber *)[obj2 lastObject]]
Basically, you are grabbing the last object out of each NSArray, which you know is an NSNumber. You use the compare function to return NSOrderedAscending, NSOrderedDescending, or NSOrderedSame. I hope that helps.
I'm often required to retrieve the 1st object belonging to a Set. (Using that object as a representative of that set.)
I envision a Collection Object operator, akin to the
#unionOfObjects
BUT clearly
#firstObject
Is it possible to create such a Collection operator!
Currently there's no way to define custom collection operators. However, due to some internal magic there is a funny solution:
NSSet *testSet = [NSSet setWithArray:#[#"one", #(1)]];
id object = [testSet valueForKey:#"#anyObject"];
NSLog(#"anyObject (%#): %#", NSStringFromClass([object class]), object);
UPD: Forgot to mention another handy trick: you can use #lastObject on NSArray!
This is doing my head in...
Simplified version: I have two text fields - Field A and Field B. Field B can be derived from field A and similarly Field B can be derived from Field A.
(There's couple of other fields that in combination with either A or B produce data for multiple TextLabels)
What I want to do is: when user changes Field A, Field B is updated, and vice versa.
So I created two methods that do A to B and B to A. And defined dependencies like:
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:#"calculatedFieldA"]) {
NSSet *dependentKeys = [NSSet setWithObjects:#"valueOfFieldB", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:dependentKeys];
}
if ([key isEqualToString:#"calculatedFieldB"]) {
NSSet *dependentKeys = [NSSet setWithObjects:#"valueOfFieldA", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:dependentKeys];
}
return keyPaths;
}
Where calculatedFieldA and calculatedFieldB are the methods that do the conversion and valueOfFieldA and valueOfFieldB are NSString's that are bound to the two text fields.
It works (but only one way, B is updated whenever A changes) if I remove the second if statement. When the second if is defined, it just bombs out, because (I think) it sees A updated so goes and updates B, then because B is updated, goes and updates A again, etc, etc...
What is the best way to achieve this circular dependency? Is it a time to start reading about ValueTransformers?
PS. I'm a Cocoa newbie so please bear with me and don't punch too hard if this is a very trivial question...
EDIT:
I probably need to clarify few points:
calculatedFieldA accepts B value and returns A, also updates (via setter method) valueOfFieldA.
Similarly calculatedFieldB accepts A value and returns B, also updates (via setter method) valueOfFieldB.
This is on Lion, with Xcode 4.1.
Value transformers are almost certainly the correct answer here. Don't store "A" and "B". Just store one of them. Value transformers are exactly the way to display and accept input for the other one.
I'm able to get things working fine with Core Data and to achieve my desired results, but I always feel it very awkward when walking to-many relationships because NSSet is, for my typical purposes, fairly useless.
An example is if I have obtained a NSManagedObject of Entity "Zoo" with attribute "nameOfZoo" and to-many relationship "animalCages", the to-many relationship pointing to Entity "AnimalCage" which has attribute "nameOfSpecies" and to-many relationship pointing to Entity "IndividualAnimal"
Zoo [nameOfZoo] ->> AnimalCage [nameOfSpecies] ->> Animals
So, getting the top level Zoo object, that's simple. But then I want to get the data for nameOfSpecies "Canus Lupus". The code I want to write is this:
// Normal NSEntityRequest or whichever it is, I have no gripe with this
NSManagedObject *zoo = ..the request to get the one Zoo..;
// I want to get the object where the key "nameOfSpecies" is set to "CanusLupus"
NSManagedObject *wolf = [[zoo animalCages] object:#"Canus Lupus" forKey:#"nameOfSpecies"];
Obviously, I can't obtain wolf in this manner. Instead, I have to write like 10 lines of code (feels like 100 lines of code) to first obtain the set, then set up a search predicate request, and declare an error variable, execute the request, then get an array of the results, then get the first element of that array.. and if I want to walk further down the tree, to find the animal named "Wolfy" for instance, then I have to do it all over again.
Am I doing things correctly or am I foolishly an easier way? I guess I can put a category on NSSet, maybe I will, but I feel like there should be a built in better way. If not, why?
If you have a data model like this:
Zoo{
name:string
animalCages<-->>AnimalCage.zoo
}
AnimalCage{
nameOfSpecies:string
zoo<<-->Zoo.animalCages
animals<-->>Animal.animalCage
}
Animal{
name:string
animalCage<<-->AnimalCage.animals
}
The to find a specific AnimalCage by name of species for a given Zoo object:
NSString *soughtSpecies=#"Canis Lupis"; // normally this variable would be passed in to the method
NSPredicate *p=[NSPredicate predicateWithFormat:#"nameOfSpecies==%#", soughtSpecies];
NSSet *wolves=[[zoo animalCages] filteredSetUsingPredicate:p];
Or you can use objectPassingTest: if you like blocks.
If you use custom NSManagedObject subclasses, then you get custom accessor methods and can use self.dot notation so the above would be:
Zoo *zoo= // fetch the appropriate zoo object
NSString *soughtSpecies=#"Canis Lupis";
NSPredicate *p=[NSPredicate predicateWithFormat:#"nameOfSpecies==%#", soughtSpecies];
NSSet *wolves=[zoo.animalCages filteredSetUsingPredicate:p];
If you know before hand that you are going to have to find cages a lot, you could wrap the above up in a method on your Zoo class e.g.
#implementation Zoo
//... other stuff
-(AnimalCage *) cageForSpecieNamed:(NSString *) specieName{
NSPredicate *p=[NSPredicate predicateWithFormat:#"nameOfSpecies==%#", specieName];
NSSet *wolves=[self.animalCages filteredSetUsingPredicate:p];
return [wolves anyObject]; // assuming one cage per species
}
Objective-c is intentionally verbose because it was supposed to be "self documenting" and the editor written for the language at the beginning had autocomplete. So, if your used to a more expressive language it might seem you are doing a lot of work but logically you aren't.