How do I update an annotation without using setCoordinate? - cocoa-touch

I have a subclassed NSManagedObject that conforms to the MKAnnotation protocol and it has NSNumber properties for latitude and longitude.
When I change any coordinates myself, I use setCoordinate: and update the latitude and longitude properties inside the implementation of setCoordinate:. Using this method, the map view updates the annotations. However, when I merge changes with another NSManagedObjectContext via mergeChangesFromContextDidSaveNotification:, setCoordinate: is not used because the latitude and longitude properties are explicitly changed. This prevents any KVO notifications going out about the coordinate changing.
I have tried to get the map view to realize the coordinate depends on the latitude and longitude properties with this code:
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([#"coordinate" isEqualToString:key]) {
NSSet *affectingKeys = [NSSet setWithObjects:#"latitude", #"longitude", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys];
}
return keyPaths;
}
However that code produces this crash:
Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer for the key path "coordinate" from because it is not registered as an observer.'
Any ideas on how to prevent that crash or alternative methods to get the coordinate KVO notification sent out when the latitude or longitude is changed? Thanks for taking a look.

I fixed this by changing setCoordinate: so that it uses the primitive accessors that are automatically generated by Core Data to set the latitude and longitude properties.
I suspect that because my keyPathsForValuesAffectingValueForKey: made the coordinate key dependent on both the latitude and longitude keys, the MKMapView was getting confused when setCoordinate: used the public accessors instead of the primitive accessors.
When the public accessor methods were used to set the latitude and longitude, any object observing the coordinate key path observed three separate changes (for the key paths coordinate, latitude, and longitude) when there was only one change (coordinate) that should be observed, which confused the map view.

It's a little overhead, but you could observe the latitude and longitude params (resp. override their setters), and force-set the coordinate (with setCoordinate:) when the change.

Related

Immutable alternative to CGPoint?

As the question states, I would prefer an immutable version of CGPoint, CGSize, and CGRect, although I would rather use a type which is supported by the iOS framework, to avoid converting between types.
So, is there any supported immutable version of CGPoint? If not, any recommendations?
Thanks!
You can just use constant CGPoints etc.
E.g.:
const CGSize size = (CGSize){6.0f, 4.0f};
Using CGSizeMake won't work as they are not compile time constants and you can't use variables, but this is the closest you'll get to an immutable CGSize. If that is not good enough then you'll need to use an object and convert when needed.
When a CGPoint (or any other struct - CGRect, etc.) is a property of an Objective-C object, the mechanics of getter/setter methods and the value semantics of C structs means that there is some level of protection against indirectly modifying the value of a CGPoint property of an object. For example, an expression like someObject.someCGPointProperty.x = 123 will not actually modify the x value of the CGPoint owned by someObject, because the someCGPointProperty accessor will return the CGPoint by value, which means that you are setting the x value on a copy of the CGPoint. To actually modify the CGPoint owned by that object, you would need to do something like:
CGPoint point = someObject.someCGPointProperty;
point.x = 123;
someObject.someCGPointProperty = point;
Regarding control over the number of instances you create: since CGPoint is a struct, it's passed around by value - effectively it's copied - in function arguments or Objective-C messages. So there's going to be plenty of copies of thees structs moving around anyway. However, these structs are created on the stack, not the heap, and a CGPoint is only 16 bytes (possibly smaller on 32-bit iOS devices?), so it's doubtful you need to worry about any performance implications.
Also, see #hypercrypt's more succinct answer regarding const values, as that may address your intentions well.
You can use the NSValue wrapper class. It can wrap a point, a size, a rect and more. It's immutable, so whatever you store inside it can't be changed (remember that if you store a pointer, the memory area to which points can still be changed).
Example
NSValue* value= [NSValue valueWithCGPoint: CGPointMake(x,y)];
If you try to set value.CGPointValue.x or y you get a compile error. The getter returns just a copy of the CGPoint held in the object, so there's no way to change it's value.

Objective C - design advice: Class to store a bodyweight value

As an aid to learning objective c/oop, I'm designing an iOS app to store periodic bodyweight measurements. I want to be able to retrieve the bodyweight in a variety of units (Kg, Lb, etc). For each bodyweight instance, can I/should I subclass NSNumber with a custom getter which return the weight in the correct unit? Perhaps I should simply subclass NSObject instead?
can I/should I subclass NSNumber with a custom getter which return the weight in the correct unit? Perhaps I should simply subclass NSObject instead?
Can you? Yes. Should you? No. NSNumber instances are actually never NSNumbers. NSNumber acts as a dispenser for it's various subclasses, which means that when you subclass it, you are essentially promising to reimplement the class dispension aspect of NSNumber (the same is true of NSArray and NSString).
Try to write a category on NSNumber rather than attempt to subclass it, and even then, NSNumber was never designed for manipulation and mathematical operations. Numbers are essentially immutable constructs, so you would be far better off writing a method that calculates things with primitives somewhere in your controller object. In your particular situation, NSNumber would only really be appropriate for persistence.
No, don't not use NSNumber at all, do not even add a category to it - this class (cluster) if designed for when you need to store a primitive type as an object and little else.
It you wish to encapsulate a weight write a class to do it, something along the lines of (code typed at terminal):
#interface Weight : NSObject
#property double kilos:
#property double pounds;
// etc
#end
#implementation Weight
{
double value; // stored in a suitable unit, kg, lb, oz, g, etc.
}
// implement getters and setters converting between unit of property and unit of value
// implement dependent property methods to setting, say, pounds produces a KVO
// notification for both pounds and kilos, etc. E.g.:
+ (NSSet *) keyPathsForValuesAffectingPounds
{
return [NSSet setWithObject:#"kilos"];
}
#end
Now you can set the value as one unit, read it as another, and get KVO notifications for all properties whenever one is set.
You'll want to add constructors (e.g. newWeightWithKilos:), maybe operations (e.g. addWeight: - which can just add the internal values), and need to decide whether a Weight is mutable or immutable.
You need not sub class the NSNumber instead you can subclass NSObject and add a property to set the weight in base unit(eg: kg's) and then you can add several methods that take the weight in base unit and returns the converter value in the units you specify(lb etc).

How to put a NSMutableArray into an entity (NSManagedObject) attribute?

I defined an entity called "Map". It has three attributes:
scale : NSNumber with NSFormatter (mandatory if you want a NSNumber instead of a NSString, which >bangs<).
units : NSString
mapData : Binary Data ? Transformed value ?
This mapData attribute causes a problem. I have a custom NSView ("matrix" property) which stores some references into a NSMutableArray. This is the array I want to put into the mapData attribute. But this does not work:
[[[mapController arrangedObjects]lastObject] setValue:[matrix colorReference]forKey:#"mapData"];
Ignore everything but this :
"lastObject": is the (single) Map entity.
[matrix colorReference] is the (property) NSMutableArray.
Now, to simplify: I want the NSMutableArray to become the "mapData" attribute of the Map entity, to be stored when the doc is saved, and later retrieved and transferred to the custom view. The design is clear, the way to do it much less…
Help me, please.
You can serialize and array and store it in Core Data by making the attribute Binary Data type which is basically of NSData * type.
Then serialize your array into NSData by using NSKeyedArchiver.
NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:myArray];
and back to NSArray using NSKeyedUnarchiver:
NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:myData];
Well, it is even more simpler, in fact.
Core Data DOES serialize the NSMutableArray into an attribute whose type is "Transformable". The transformation is of course reversible, so I can use it directly to set the property of my custom view, using just:
[matrix setColorReference:[[mapController content]valueForKey:#"mapData"]];
where "matrix" is the NSView, "colorReference" its NSMutableArray property, "map controller" the entity which stores the NSData with the key "mapData".
For the reverse operation (an edited map to save on the file), I do:
[[mapController content] setValue:colorReference forKey:#"mapData"];
As the attribute of Core Data is KVO compliant, the file is marked as edited and, of course, there is possible to undo!
For keeping references between objects (controller --> view and reverse) I simply use IBOutlet properties in my subclasses, make the bindings in IB and let the frameworks do the rest (better than I would).
Thanks!

Converting an Nsmutable array into Mkannotation array

Hello all I was playing with core-data ....I have an entity which contains latitude longitude and some description information now for this entity i created an Nsmanaged object class and then assigned in property tag.
Now i have successfully parsed an XML put them into core-data and drill downed the data from core-data into Nsmuatble array now array has some objects in it and i cannot add the objects to map (i know the reason why it does not add,coz the array is not type of mkannotation ).
Now is there any chance i can create a entity which is a class of mk annotation .Or else i will create a one more Mkannotation object class and pass down the data and convert it into mk annotation class
Your custom managed subclass needs to implement the MKAnnotationProtocol which is not the same thing as being an MKAnnotation object.
You need to declare that it conforms to the protocol with a <MKAnnotation> in the class interface declaration, and then you need to provide an implementation of the coordinate property to return a CLLocationCoordinate2D from the properties of your object.

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".