Objective-C forwardInvocation:

时间:2011-07-07 17:19:06

标签: objective-c nsobject method-missing

我经常这样做:

CoolViewController *coolViewController = [[CoolViewController alloc] init];
[self.navigationController pushViewController:coolViewController animated:YES];
[coolViewController release];

我如何在UINavigationController类别中覆盖forwardInvocation:,以便我可以改为:

[self.navigationController pushCoolViewControllerAnimated:YES];
  1. 请在答案中注明相关代码,而不仅仅是解释。谢谢!

  2. 欢迎评论这是否是一种好习惯。我也问这个用于教育目的,但在我看来,在这种情况下,代码的简化可能会超过处理时间和时间的不明显(正确?)成本。内存使用情况。此外,我来自Ruby背景,喜欢使用动态编程来简化事情,例如Rails中的动态查找器(例如find_by_name)。

  3. 如果您可以实现pushCoolViewControllerAnimated:withBlock并在初始化视图控制器后调用该块,则允许我在创建的视图控制器上设置某些实例变量。

  4. 更新:我记得ARC即将推出。因此,这个具体的例子可能不那么有用,但仍然是可以在其他情况下使用的一个很好的练习/例子,例如,Core Data&的动态查找器。传递一个块来配置NSFetchRequest

1 个答案:

答案 0 :(得分:14)

使用Objective-C运行时编程指南中描述的动态方法解析机制,具体来说,+[NSObject resolveInstanceMethod:]

@implementation UINavigationController (FWD)
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSString *name = NSStringFromSelector(sel);
    NSString *prefix = @"push";
    NSString *suffix = @"Animated:";
    if ([name hasPrefix:prefix] && [name hasSuffix:suffix]) {
        NSRange classNameRange = {[prefix length],
            [name length] - [prefix length] - [suffix length]}
        NSString *className = [name substringWithRange:classNameRange];
        Class cls = NSClassFromString(className);
        if (cls) {
            IMP imp = imp_implementationWithBlock(
            ^(id me, BOOL animated) {
                id vc = [[cls alloc] init];
                [me pushViewController:vc animated:animated];
                [vc release];
            });
            class_addMethod(cls, sel, imp, "v@:c");
            return YES;
        }
    }
    return [super resolveInstanceMethod:sel];
}
@end

当然,如果UINavigationController已使用+resolveInstanceMethod:,那么您现在已经破坏了它。在UINavigationController的子类中执行此操作,或使用方法调配来启用调用原始实现,将解决该问题。

接受创建后块的版本是一个简单的扩展(更改块参数,更改类型编码,更改选择器名称模式以及如何提取目标类名称)。