0

この記事によると、Objective CのForループは、SELとIMPを使用して最適化できます。私は少し前からこのアイデアをいじっていましたが、今日はいくつかのテストを試しています。ただし、あるクラスでは機能しているように見えますが、別のクラスでは機能していないようです。さらに、スピードアップがどのように正確に発生するのか知りたいですか?objC_mesgSentを回避することによって

質問1 これはどうですか:

Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        [self addSubview:cell.view];
        [self.cells addObject:cell];

これより悪い:

SEL addCellSel = @selector(addObject:);
IMP addCellImp = [self.cells methodForSelector:addCellSel];
Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        [self addSubview:cell.view];
        addCellImp(self.cells,addCellSel,cell);

質問2 なぜこれが失敗するのですか?(selfはUIViewから継承するクラスであることに注意してください)

SEL addViewSel = @selector(addSubview:);
IMP addViewImp = [self methodForSelector:addViewSel];
Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        addViewImp(self,addViewSel,cell.view);
        [self.cells addObject:cell];

エラー:

キャッチされなかった例外'NSInvalidArgumentException'が原因でアプリを終了しています、理由:'-[SimpleGridView addSubview:]:認識されないセレクターがインスタンス0xaa3c400に送信されました'

クラス"SimpleGridView"にメソッドaddSubviewが見つからないことを教えてくれます。しかし、私が試したとき:

if ([self respondsToSelector:addViewSel]){
    NSLog(@"self respondsToSelector(AddViewSel)");
    addViewImp(self,addViewSel,cell.view);
} else {
    NSLog(@"self does not respond to selector (addViewSel");
   [self addSubview:cell.view];  
}

それでもまったく同じエラーが発生します!

質問3 セレクターと実装を次のようにClassInit/newメソッドに設定できないのはなぜですか。

iContactsGridCell *cell;
SEL initCellSel = @selector(initWithRect:);
IMP initCellImp = [iContactsGridCell methodForSelector:initCellSel];
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = initCellImp([iContactsGridCell new],initCellSel,tmp_frame);

参考:クラスiContactsGridCellは、クラスCellを継承します。これは、クラスCellを定義し、実装します。

- (id) initWithRect:(CGRect)frame;

さらに、キャストは役に立ちませんでした(認識されないセレクターに関する同じエラー)

iContactsGridCell *cell;
SEL initCellSel = @selector(initWithRect:);
IMP initCellImp = [Cell methodForSelector:initCellSel];
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = (iContactsGridCell *)initCellImp([Cell new],initCellSel,tmp_frame);

次のようなさまざまな組み合わせを試してください。

IMP initCellImp = [Cell methodForSelector:initCellSel];

または

cell = initCellImp([iContactsGridCell class],initCellSel,tmp_frame);

まったく同じエラーを生成します。では、何が欠けているのか、これはどのように有益であり、クラスinitメソッドにIMP / SELを使用することは可能ですか?また、C関数ポインターはこれに比べて高速ですか、それとも上記のすべてが単にObjective-C関数ポインターラッパーですか?ありがとうございました !PS:一度に質問が多すぎる場合は、お詫び申し上げます。

4

2 に答える 2

4

そのコードではobjc_msgSend()、測定可能な量のオーバーヘッドが発生する方法はありません。割り当て、ビュー階層の操作、その他の操作など、非常にコストがかかる可能性のある他の作業が非常に多く行われています。

つまり、直接関数呼び出しに移行することは、時間の無駄です。

コードが失敗する理由は明らかではありません。の戻り値をチェックしmethodForSelector:て、意味があることを確認する必要があります。また、そのような方法でIMPを呼び出すのは正しくないことに注意してください。それは、それが本当にある特定の種類の関数ポインタに型キャストする必要があります。つまりvoid (*impPtr)(id, SEL, CGRect) ...、何でも。

最後に、これを高速化するには、多くのビューの使用から離れて、もう少しオンデマンドの何かに移行する必要があります。これは、アーキテクチャによるパフォーマンスの問題のようです。

于 2012-12-21T21:50:02.620 に答える
2
  1. はい、それは回避します。これはobjc_msgSend()、(より重要なことに)実行時にレシーバーのクラスとセレクターに基づいて適切なIMPのルックアップを実行する必要がありますが、この場合、レシーバーのクラスとセレクターは毎回同じであるため、理論的には一度調べて再利用するだけです。

  2. あなたが説明したことから何が悪いのかを知るのは難しいです。1つの可能性は、オブジェクトのクラスがメソッドを動的に処理することです。たとえば、特定のセレクターの特定の実装はありませんが、呼び出されると、実際に処理できます(たとえば、を使用forwardInvocation:)。これは、プロキシオブジェクトや、受信したものを複数のターゲットに送信する通知オブジェクトなど、多くの場所で使用されます。そして、そのようなクラスはそれに応答するので、と言うかもしれませYESrespondsToSelector:。これがここで起こっていることかどうかはわかりません。

  3. [iContactsGridCell methodForSelector:initCellSel]は、クラスオブジェクトを呼び出しているため、その名前のクラスメソッドを検索します。その名前のインスタンスメソッドを取得するには、のインスタンスを呼び出すか、クラスのメソッドを使用する必要があります。methodForSelector:iContactsGridCellmethodForSelector:iContactsGridCellinstanceMethodForSelector:

于 2012-12-22T00:28:23.617 に答える