-validateMenuItem: all the time, particularly when there are inheritance hierarchies involved.There's a pattern that can help you get it right much more easily using introspection. You can implement your
-validateMenuItem: method to ask the receiver whether it can respond to a particular selector at the moment, like so:- (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
{
BOOL enabled = YES;
SEL itemAction = [menuItem action];
if (itemAction != nil) {
if ([self respondsToSelector:itemAction]) {
SEL canAction = [self canSelectorForAction:itemAction];
if ((canAction != nil) && [self respondsToSelector:canAction]) {
BOOL (*performCanAction)(id, SEL) = (BOOL (*)(id, SEL)) objc_msgSend;
enabled = performCanAction(self, canAction);
} else {
enabled = YES;
}
}
}
return enabled;
}
- (SEL)canSelectorForAction:(SEL)action
{
NSString *actionString = NSStringFromSelector(action);
// actionString is guaranteed to be 2+ characters
NSString *actionStringFirst = [actionString substringToIndex:1];
NSString *actionStringBody = [actionString substringWithRange:
NSMakeRange(1, [actionString length] - 2)];
NSString *canString
= [[@"can" stringByAppendingString:[actionStringFirst uppercaseString]]
stringByAppendingString:actionStringBody];
return NSSelectorFromString(canString);
}The above checks whether or not the receiver responds to the item's action selector. If it does, it will check if the receiver also implements a -(BOOL)canActionName selector, and use that to tell whether the menu item should be enabled.The
-canSelectorForAction: method just takes an action selector and returns its equivalent "canAction" selector, by upper-casing the first letter and appending it to "can". This lets you write a pair of methods for each action, one to perform the action and one to tell whether the action can be performed at the moment, leading to easy-to-test and uncluttered validation code.Update (June 19, 2008): Changed how the
-(BOOL)canActionName selector is performed to be safe and correct on Intel, and other architectures where a (BOOL) return might not be exactly equivalent to an (id) return. Thanks to an anonymous commenter for the correction!![[info]](http://l-stat.livejournal.com/img/userinfo.gif)
Compiler Warning
(Anonymous)
2008-06-17 03:28 pm (UTC)