Question

Context: I have several CGPathRef's in a custom class, derived from NSObject, named model. I'm looking for a way to return a specific CGPathRef, based one a string I generate at runtime.

Simplified Example, if I could use KVC:

#model.h
@property (nonatomic) CGMutablePathRef pathForwardTo1;
@property (nonatomic) CGMutablePathRef pathForwardTo2;
@property (nonatomic) CGMutablePathRef pathForwardTo3;
...


#someVC.m
-(void)animateFromOrigin:(int)origin toDestination:(int)destination{
    int difference = abs(origin - destination);
        for (int x =1; x<difference; x++) {
            NSString *pathName = [NSString stringWithFormat:@"pathForwardTo%d", x];
            id cgPathRefFromString = [self.model valueForKey:pathName];
            CGPathAddPath(animationPath, NULL, cgPathRefFromString);
        }
}

Question: How can I access non-KVC compliant properties (CGPathRef) with just their name represented as a string?

Was it helpful?

Solution

You should be able to use NSInvocation for this. Something like:

// Assuming you really need to use a string at runtime. Otherwise, hardcode the selector using @selector()
SEL selector = NSSelectorFromString(@"pathForwardTo1");
NSMethodSignature *signature = [test methodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector];
[invocation setTarget:test];
[invocation invoke];

CGMutablePathRef result = NULL;
[invocation getReturnValue:&result];

You can also do it directly with the proper objc_msgSend variant, but NSInvocation is easier (though probably significantly slower).

EDIT: I put a simple little test program here.

OTHER TIPS

This was discussed at length on cocoa-dev. The short answer is: you can't directly.

But it often turns out that there are a lot of ways around that. In your specific example, you should be storing these in an array, and then you wouldn't even need the KVC call.

Again, for your specific example, another possible solution is to switch to UIBezierPath, which can be converted to and from CGPath as you need.

As a more generic solution, you could wrap these into an object that just holds the value for you, and KVC on that. This is basically how NSValue is often used for wrapping raw pointers.

The totally generic solution is that you can implement valueForUndefinedKey:, inspect the key, and return what you want. You could, for instance, stick the paths in an array and index into it based on the key given. Or you could stick them in a dictionary and look them up there (this is basically what CALayer does). And of course if you really needed it to be dynamic, you could introspect with the runtime and basically reimplement KVC… it would be very rare that this would be the right answer. Pretty much all of these "generic" solutions are overkill for most problems.

The short answer is that KVC does not always play well with low-level objects that aren't toll-free bridged.

What I would do (and have sometimes done) is wrap the CGPathRef in a UIBezierPath exactly so that it is an object and has all the benefits accruing thereto, including KVC, ability to stick it in an NSArray or NSDictionary, and ARC memory management. That, after all, is exactly what a UIBezierPath is (more or less).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top