4

私はよく次のようなことをします:

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

のカテゴリでUINavigationControllerオーバーライドforwardInvocation:して、代わりに次のことができるようにするにはどうすればよいですか。

[self.navigationController pushCoolViewControllerAnimated:YES];
  1. 説明だけでなく、関連するコードを回答に含めてください。ありがとうございました!

  2. これが良い習慣であるかどうかについて自由にコメントしてください。私は教育目的でもこれを求めていますが、この場合、コードの単純化は、処理時間とメモリ使用量の目に見えない (正しい?) コストを上回る可能性があるようです。find_by_nameまた、私は Ruby のバックグラウンドを持っており、Railsの動的ファインダー (例: ) など、物事を単純化するために動的プログラミングを使用するのが大好きです。

  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か、メソッドswizzlingを使用して元の実装を呼び出すことができるようにすると、その問題が解決されます。

作成後のブロックを受け入れるバージョンは単純な拡張です(ブロックパラメーターの変更、タイプエンコーディングの変更、セレクター名のパターンの変更、および目的のクラス名の抽出方法を変更します)。

于 2011-07-07T18:41:06.330 に答える