39

So, my scenario is this:

I have an NSManagedObject subclass in my iOS application, and as a property I want to store the contents of an MKPolygon object. The way I've decided to go about this (and whether it's valid is perhaps a different question) is to declare the polygon property as a transformable object, then store an NSArray containing the polygon's points (as an NSValue object).

To this end, I've written a couple of convenience class methods on my model object:

+ (NSArray *)coordsArrayFromMKPolygon:(MKPolygon *)polygon pointCount:(int)count
{
    CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(sizeof(CLLocationCoordinate2D) * count);
    [polygon getCoordinates:coords range:NSMakeRange(0, count)];
    NSMutableArray *coordsArray = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        NSValue *coordVal = [NSValue valueWithBytes:&coords[i] objCType:@encode(CLLocationCoordinate2D)];
        [coordsArray addObject:coordVal];
    }
    free(coords);
    return [NSArray arrayWithArray:coordsArray];
}

+ (MKPolygon *)polygonFromCoordsArray:(NSArray *)coordsArray pointCount:(int)count
{
    CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(sizeof(CLLocationCoordinate2D) * count);
    for (int i = 0; i < count; i++) {
        CLLocationCoordinate2D coord;
        [[coordsArray objectAtIndex:i] getValue:&coord];
        coords[i] = coord;
    }
    free(coords);
    return [MKPolygon polygonWithCoordinates:coords count:count];
}

I can just call these methods on my MKPolygon objects before saving or loading an instance of the model, but I'd like to override the dynamic getters and setters in the model itself, so that I can just say something like [turf setTurf_bounds:polygon] (where polygon is an MKPolygon instance).

What I'd really like is to be able to do something like this:

- (void)setTurf_bounds:(id)turf_bounds
{
    MKPolygon *poly = (MKPolygon *)turf_bounds;
    NSArray *coordsArray = [Turf coordsArrayFromMKPolygon:poly pointCount:[poly pointCount]];
    // Save the converted value into the @dynamic turf_bounds property
}

- (id)turf_bounds
{
    // grab the contents of the @dynamic turf_bounds property into say, NSArray *coordsArray
    return [Turf polygonFromCoordsArray:coordsArray pointCount:[coordsArray count]];
}

But I've had no joy so far. Calling [super setValue:coordsArray forKey:@"turf_bounds"] or its getter counterpart doesn't work, nor does trying to write it as self.turf_bounds (which just recursively calls my overridden setters).

Am I going about this completely the wrong way, or just missing something?

HowlingEverett
  • 820
  • 1
  • 9
  • 10

1 Answers1

76

Never call [super valueForKey:..] in a NSManagedObject subclass! (unless you implemented them in a superclass yourself) Instead use the primitive accessor methods.

Sample getter/setter implementation from ADC:

@interface Department : NSManagedObject
@property(nonatomic, strong) NSString *name;
@end

@interface Department (PrimitiveAccessors)
- (NSString *)primitiveName;
- (void)setPrimitiveName:(NSString *)newName;
@end


@implementation Department

@dynamic name;

- (NSString *)name
{
    [self willAccessValueForKey:@"name"];
    NSString *myName = [self primitiveName];
    [self didAccessValueForKey:@"name"];
    return myName;
}

- (void)setName:(NSString *)newName
{
    [self willChangeValueForKey:@"name"];
    [self setPrimitiveName:newName];
    [self didChangeValueForKey:@"name"];
}

@end
Martin Brugger
  • 3,168
  • 26
  • 20
  • 2
    Primitive accessors - that's what I was missing, thanks! Plus, this method lets me explicitly require MKPolygon (or MKMultiPoint, or MKAnnotation) for the setter rather than passing as id and casting. I'd figured that I shouldn't be calling valueForKey, but I hadn't found the correct alternative. Cheers :) – HowlingEverett May 15 '11 at 14:21
  • Does this exactly applies to overriding dynamic properties defined in a superclass? – Rivera Sep 16 '14 at 07:23
  • This will/didChangeValueForKey convention existed long before @dynamic properties were introduced and are necessary to trigger observings. There is no need to use this kind of implementation if you do not observer the properties. So I would say it depends on use case. (@Rivera sorry for reverting your change in the first place, with current versions of obj-c it is possibly cleaner to declare the privimitive getter/setter) – Martin Brugger Sep 16 '14 at 10:58
  • You say: "Never call `[super valueForKey:..]` in an `NSManagedObject` subclass!". Why is this? It goes horribly wrong, I can see that, but why? – Benjohn Sep 25 '15 at 13:29
  • 1
    It does not work overriding setter of relationship property – 93sauu Nov 24 '16 at 10:50