When manually triggering a KVO event, can the change dictionary be amended? - iphone

I have a data model, composed mostly of an NSMutableArray 'contents' and NSMutableDictionary 'contentsByName'. I have ViewController objects that I wish to observe changes in the data model.
I have a property "count" on the model that returns the size of the array 'contents' and I can trigger a KVO change observation with willChange: and didChange:. So far, so good. However, the view-controllers are now aware that the model has changed, but do not know what's been added to it. Ideally, I need to put extra information into the change dictionary that's delivered to the observer.
Is this at all possible?

This is easily solvable by updating the model objects in a more granular way; however, built-in collections don't generate KVO notifications when their contents are modified, and will require some manual support.
If you want to generate notifications about changes to the array, use willChange:valuesAtIndexes:forKey: and didChange:valuesAtIndexes:forKey: whenever it's modified. When these methods are used, the change dictionary will contain an entry for NSKeyValueChangeIndexesKey, which reflects the indexes of insertion, deletion, or replacement.
If you want to generate notifications about changes to the dictionary, you can call willChangeValueForKey: and didChangeValueForKey: on the dictionary itself, like so:
- (void)addContent:(id)content {
NSString *key = [content name];
[self.contentsByName willChangeValueForKey:key];
[self.contentsByName setValue:content forKey:key];
[self.contentsByName didChangeValueForKey:key];
}
Any observer can also use NSKeyValueObservingOptionNew or NSKeyValueObservingOptionOld to receive the new or old values, respectively.

Related

How to notify other objects of changes in objective c

Suppose I have an object called ColorHolder where one of its properties is a UIColor value. Suppose I also have an NSMutableArray that stores some UIColors, let's call it colors. I initialize a ColorHolder object with a value from the array as follows:
[[ColorHolder alloc] initWithColor: [self.colors objectAtIndex:5]];
Now at some point later in the program, the user changes the value in colors at that same index 5. What I would like is the ColorHolder object I initialized above, for its color value to also change when its corresponding value in the colors NSMutableArray changes. How can I set up a pointer in this way so it happens?
The views which you set the font (and the FontHolder whatever that does) are not "observing" changes to the font array and the UIFont class is immutable.
You will need to use NSNotificationCenter to add an observing class which monitors this array and sends notifications to the views (and/or FontHolder(s)) which are interested.
This is a classic observer pattern.
http://blog.csdn.net/dadalan/article/details/4240733
The above blog is not a copy and paste answer for your code, but I provide it for information on the pattern in general.
Also if your really not needing to update views you could simply tell FontHolder to refresh itself from the font array anytime the user has made a change to it. Which if there is only one observer is a cheap way to implement the observer pattern.

How to validate the values in the context before saving to core data

How can we validate the context before saving it to core data?. My idea is i should have some validation before saving it into core data, if the values doesn't satisfy the validation the the coredata should not save the values. Say for example i have attributes like name, class, age, etc for entity candidate. I should have a validation that the values shouldn't be nil. If it is nil then the other values should not be saved.
Can anybody help me in this regard
EDITED:
I need to check them only at the time of saving and that should be done with core data
I like to do catchall validation in the save routine. Before you actually do the call to save the context, loop through its insertedObjects array and make sure they are as you require. If they aren't, you can either delete them or alert the user that they need to complete something (if the latter, return out of the method; give the user a chance to fix the problem).
Other validation should be at the point of entry, when you are getting values from, say, a textfield or checkbox to assign to your managed objects. Let the user know right away if there's a problem.
Also check out NSNumberFormatter, which can be applied to fields, preventing user from making incorrect entries to begin with.
Coredata validate itself when inserting its values. In managedObject class we can write our custom validation so that coredata will check that validation before saving the values. If the value is not valid then those values in the context will not be saved to coredata.
Here i added
#interface
-(BOOL) validateForInsert:(NSError **)error;
#implementation
-(BOOL) validateForInsert:(NSError **)error {
// check the value of the field with validation
if(condition == true) {
return Yes;
}
return NO;
}
(NSError **) is a special parameter that makes the coredata to call this method as if like a delegate method
Sorry, I hadn’t read your question carefully enough when I made that first answer. You’re not validating that individual entries for individual attrs are correct, rather, that no changes should be saved unless all attrs are filled for that object.
Looking at Apple doc “Model Object Validation”, you are concerned with inter-property validation, not property validation, and you are on the right track to be thinking of using validateForInsert: for this purpose.
That doc also supplies examples. Here’s a possible implementation for the particular entity you describe:
- (BOOL)validateForInsert:(NSError **)error {
if (self.name && self.class && self.age)
return [super validateForInsert:error];
else
return NO;
}
However, this method happens at the insertion stage, not at the save stage.
If you are gathering entries for a new entity all at once, validating at the insertion stage would make sense — don’t add a new object to the context if that object is doomed to be discarded as incomplete.
If you are gathering entries for changes to an existing object, and you want to make sure that all those changes work together before accepting any of them, validateForUpdate: would make sense — but there would be no way to restore the object to its original state other than by reopening the context without saving, unless you had cached its original values elsewhere.
If you want to gather attrs individually and wait to check that they are all complete before saving the object, I think you would do as I first suggested: Loop through the context’s insertedObjects and take care of validation before actually saving the context. There’s no existing validateForSave: method to override, but you could add one.
You could also combine these techniques: Gather entries and make new objects without inserting them, but cache all these objects in an array. When it comes time to save, loop through the cache and insert the objects into the context only if they pass validateForInsert:; then save the context.
Obviously I’m learning along with you, so the above might not be quite the cookie. Hopefully that Apple doc will be enough to get you started.

Alternatives to NSMutableDictionary?

I have 3 NSMutableDictionaries that hold data for 3 separate UITableViews. When a value is checked from one of the UITableViews, I want to hold that value so depending on the different values that are checked from the different tables, I can generate an answer on the next page. I thought maybe I could create a new NSMutableDictionary that has all the possible selections, and then when a user hits the checkbox, to tell my newNSMutableDictionary that that value has been selected. But I don't think it works that way since it's a key-value-pairing. I was wondering if there were alternatives to this, or if someone had a good way of holding this information? Thanks.
Instead of doing this, you can try having one NSMutableArray of the selected NSIndexPath objects.
This way, you'd have a pretty lightweight memory footprint (lazy loading) and if you needed to grab the actual cell's value, you can ask the UITableView with -tableView:cellForRowAtIndexPath:
Or, even better, have one NSMutableDictionary with one the keys being the tag of the specific tableview wrapped in an NSNumber and the value being an NSMutableArray of selected index paths.
To retrieve the selected index paths would be as simple as this:
NSArray *indexPaths = [selectedDict objectForKey:[NSNumber numberWithInt:tableView.tag]];
for(NSIndexPath *p in indexPaths) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:p];
//do something with cell...
}
why not store all possible selections in a NSDictionary, initialise it with the all the selections a user can make. When the user makes a selection add that item to a "selectedItems" NSMutableDictionary. This way your NSMutableDictionary (that contains selections) will only ever have the selected items in it, you can then add and remove from this dictionary without needing to mutate the non-mutable NSDictionary that contains the selections that a user can make..?
I'm not sure if I've understood your problem right.
But, if your data has the "shape" of a tree you might consider to save the information in this way.
You can put a Dictionary inside your Dictionary
You can put your data in CoreData and use NSTreeController

Strange problem with NSMutableArray - Possibly some memory corruption

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.

How to store object + string pairs without retaining the objects, and what kind of storage to use?

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.