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