2

ヘッダーファイルにアクセスできないクラスにカテゴリを追加する方法はありますか?

テストの目的で、にカテゴリを追加したいのですUITableViewCellDeleteConfirmationControlが、クラスは(私が知る限り)プライベートフレームワークの一部です。

どうやってやるの?


精緻化(mihiriosの要求による):

フランクテストフレームワークを拡張して、を削除しようとしたときに表示される確認ボタン(大きな赤い[削除]ボタン)のタップをシミュレートしようとしていますUITableViewCelltapフランクはにメソッドを追加しUIControlます。UITableViewCellDeleteConfirmationControl何らかの理由で、コントロールをタップするフランクの通常の方法は、クラス(サブクラス)では機能しませんUIControl

回避策を作成しました。UITableViewCell次の方法で、にカテゴリを追加しました。

- (BOOL)confirmDeletion {
    if (![self showingDeleteConfirmation]) {
        return NO;
    }
    UITableView *tableView = (UITableView *)[self superview];
    id <UITableViewDataSource> dataSource = [tableView dataSource];
    NSIndexPath *indexPath = [tableView indexPathForCell:self];
    [dataSource tableView:tableView
       commitEditingStyle:UITableViewCellEditingStyleDelete
        forRowAtIndexPath:indexPath];
    return YES;
}

これにより、テーブルのデータソースが検出され、そのtableView:commitEditingStyle:forRowAtIndexPath:メソッドが呼び出されます。これは、(のドキュメントによるとUITableView)ユーザーが確認ボタンをタップしたときにシステムが実行する操作です。

これは機能しますが、フランクのデフォルトのボタンをオーバーライドして、メソッドをUITableViewCellDeleteConfirmationControl追加することにより、タップ可能なボタンのように見せたいと思います。tapこのtapメソッドは、確認ボタンを含むセルを見つけて、を呼び出し[cell confirmDeletion]ます。

のカテゴリを宣言しようとするとUITableViewCellDeleteConfirmationControl、コンパイラは「インターフェイス'UITableViewCellDeleteConfirmationControl'を解決できない」と文句を言います。

誰かがclass-dumpを使用して生成したヘッダーファイルを使用しようとすると、リンカーはシンボル_OBJC_CLASS _ $_UITableViewCellDeleteConfirmationControlが見つからないと文句を言います。

4

3 に答える 3

2

テストの目的で、いつでもを使用してクラスオブジェクトを取得し、ランタイムメソッドをNSClassFromString使用して必要なことを行うことができます。class_replaceMethod詳細については、Objective-Cランタイムリファレンスを参照してください。

于 2012-04-28T07:14:57.617 に答える
2

私の知る限り、カテゴリを使用することはできませんが、実行時に手動でメソッドを追加することはできます。

これを行うための可能な方法は、新しいクラスを作成し、必要なメソッドを実装し、適切なobjc-runtime関数を使用してこのメ​​ソッドをUITableViewCellDeleteConfirmationControlに送信することです。オーバーロードの場合に後で使用するために元の関数を保存するなど、注意すべき点がいくつかあります。また、「カテゴリ」クラスでは、スーパーを呼び出したいときに注意を払う必要があります。これは機能しないため、次のようになります。代わりにobjc-runtime関数objc_msgSendSuperを使用します。

スーパーを呼び出す必要がない限り、これで問題ありません。

#import <objc/runtime.h>
#import <objc/message.h>

void implementInstanceMethods(Class src, Class dest) {
    unsigned int count;
    Method *methods = class_copyMethodList(src, &count);

    for (int i = 0; i < count; ++i) {
        IMP imp = method_getImplementation(methods[i]);
        SEL selector = method_getName(methods[i]);
        NSString *selectorName = NSStringFromSelector(selector);
        const char *types = method_getTypeEncoding(methods[i]);

    class_replaceMethod(dest, selector, imp, types);        
    }
    free(methods);
}

メソッドを呼び出すのに適したポイントは、main.mにあります。次に例を示します。

@autoreleasepool {
        implementInstanceMethods([MyCategory class], NSClassFromString(@"UITableViewCellDeleteConfirmationControl"));
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([YourAppDelegate class]));
}

しかし、なぜコントローラークラスの確認処理を移動しないのかわかりません。

于 2012-04-28T07:24:33.387 に答える
0

コンパイラが(最終的に)問題のクラスにリンクできる限り、そのクラスのカテゴリを作成できます。元のクラスのソースにアクセスできないように見えるため、より重要な質問は、カテゴリをどのように設計するかです。

于 2012-04-28T07:00:07.677 に答える