Ray Pendergraph Ray Pendergraph - 4 months ago 7
Objective-C Question

Bulding property paths programatically with another class's selectors. How does this work?

I have a method I that takes a variable number of

@selector()
values and returns a proper NSString I found on this site a while back. This is useful when building property paths for me because I don't like the idea of using strings for this and like the idea of the compiler being able to check the values the path is built from. The method is:

+(NSString *)keyPathFromSelectors:(SEL)firstArg, ...
{
NSMutableArray *keys = [NSMutableArray array];

va_list args;
va_start(args, firstArg);

for (SEL arg = firstArg; arg != nil; arg = va_arg(args, SEL))
{
[keys addObject:NSStringFromSelector(arg)];
}

va_end(args);

return [keys componentsJoinedByString:@"."];
}


This works beautifully but the question is: Why? If I have property on my current object named
person
and it has a
name
the path would be
person.name
obviously and the call to my method would look like:

+(NSSet *)keyPathsForValuesAffectingFoo
{
return [NSSet setWithObject:[self keyPathFromSelectors:@selector(person), @selector(name),nil]];
}


The
name
selector is (should not) technically visible in my current class, it's in the
Person
class to which I have a reference so how am I accessing the correct
name
SEL object?

Answer

Selectors are do not carry the guarantee that the method actually exists on the object in question. You can get compiler warnings that a selector name doesn't exist anywhere, but you won't get assurance that it does exist on the object it will be used on. For example, this compiles:

[person performSelector: @selector(applicationDidBecomeActive:)]

Even though applicationDidBecomeActive exists on your app delegate and not person.

However! This kind of thing is possible through compiler macros. Look at the libextobjc library which provides exactly the kind of selector/key path compile time checking you're looking for. This is what powers a lot of the keypath magic in ReactiveCocoa but it can also be used standalone.