我经常这样做:
CoolViewController *coolViewController = [[CoolViewController alloc] init];
[self.navigationController pushViewController:coolViewController animated:YES];
[coolViewController release];
我如何在UINavigationController
类别中覆盖forwardInvocation:
,以便我可以改为:
[self.navigationController pushCoolViewControllerAnimated:YES];
请在答案中注明相关代码,而不仅仅是解释。谢谢!
欢迎评论这是否是一种好习惯。我也问这个用于教育目的,但在我看来,在这种情况下,代码的简化可能会超过处理时间和时间的不明显(正确?)成本。内存使用情况。此外,我来自Ruby背景,喜欢使用动态编程来简化事情,例如Rails中的动态查找器(例如find_by_name
)。
如果您可以实现pushCoolViewControllerAnimated:withBlock
并在初始化视图控制器后调用该块,则允许我在创建的视图控制器上设置某些实例变量。
更新:我记得ARC即将推出。因此,这个具体的例子可能不那么有用,但仍然是可以在其他情况下使用的一个很好的练习/例子,例如,Core Data&的动态查找器。传递一个块来配置NSFetchRequest
。
答案 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
的子类中执行此操作,或使用方法调配来启用调用原始实现,将解决该问题。
接受创建后块的版本是一个简单的扩展(更改块参数,更改类型编码,更改选择器名称模式以及如何提取目标类名称)。