4

我经常做这样的事情:

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

在 的类别中UINavigationController,我将如何覆盖forwardInvocation:,以便我可以这样做:

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

  2. 随意评论这是否是好的做法。我也是出于教育目的而要求这样做,但在我看来,在这种情况下,代码的简化可能会超过处理时间和内存使用方面的不明显(正确?)成本。另外,我来自 Ruby 背景,喜欢使用动态编程来简化事情,例如find_by_nameRails 中的动态查找器(例如 )。

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

更新:我只记得 ARC 即将推出。所以这个特定的例子可能没有那么有用,但仍然是一个很好的练习/例子,可以在其他情况下使用,例如,核心数据的动态查找器和传递一个块来配置NSFetchRequest.

4

1 回答 1

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操作,或使用方法调配来启用调用原始实现,将解决该问题。

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

于 2011-07-07T18:41:06.330 回答