iOS-複数の引数とafterDelayを使用してperformSelectorを実装する方法


90

私はiOS初心者です。私は次のようなセレクターメソッドを持っています-

- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second
{

}

私はこのようなものを実装しようとしています-

[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second" afterDelay:15.0];

しかし、それは私に言ってエラーを与えます-

Instance method -performSelector:withObject:withObject:afterDelay: not found

私が欠けているものに関するアイデアはありますか?

回答:


142

個人的には、あなたのニーズにより近い解決策はNSInvocationの使用だと思います。

次のようなものが機能します。

indexPath dataSourceは、同じメソッドで定義された2つのインスタンス変数です。

SEL aSelector = NSSelectorFromString(@"dropDownSelectedRow:withDataSource:");

if([dropDownDelegate respondsToSelector:aSelector]) {
    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[dropDownDelegate methodSignatureForSelector:aSelector]];
    [inv setSelector:aSelector];
    [inv setTarget:dropDownDelegate];

    [inv setArgument:&(indexPath) atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
    [inv setArgument:&(dataSource) atIndex:3]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation

    [inv invoke];
}

2
同意した。正解です。非常に役立つソリューション。特に、複数の引数を含むメソッドのシグネチャを変更することが許可されていない私の場合。
AbhijeetMishra

これは素晴らしいソリューションのように見えます。この手法で呼び出されているメソッドから戻り値を取得する方法はありますか?
David Pettigrew、2014

15
この手法で遅延をどのように指定しますか?
death_au 14年

4
@death_auの代わりにinvokeを呼び出す: [inv performSelector:@selector(invoke) withObject:nil afterDelay:1]; 私はこれが素晴らしい解決策であることを認めなければなりません みんなでコーディングしてください!
Maxim Chetrusca、2014年

2
会話にはちょっと遅れましたが、質問があります。dropDownDelegateとは何ですか?
ミネストローネスープ

98

[NSObject performSelector:withObject:withObject:afterDelay:]メソッドというものはないから。

送信するデータをいくつかの単一のObjective Cオブジェクト(たとえば、NSArray、NSDictionary、いくつかのカスタムObjective Cタイプ)にカプセル化し、それを[NSObject performSelector:withObject:afterDelay:]よく知られたメソッドに渡す必要があります。

例えば:

NSArray * arrayOfThingsIWantToPassAlong = 
    [NSArray arrayWithObjects: @"first", @"second", nil];

[self performSelector:@selector(fooFirstInput:) 
           withObject:arrayOfThingsIWantToPassAlong  
           afterDelay:15.0];

afterDelayパラメータを削除してもエラーになりません。つまり、afterDelayを複数のパラメーターで使用することはできませんか?
スーチ

1
エラーは発生しませんが、実行時に "selector not found"例外が発生する(そして、実行しようとしているものが呼び出されない)ので、試してみてください。:-)
Michael Dautermann、2011

ここでブール型を渡すにはどうすればよいですか?
-virata

これをObjective Cスタイルのオブジェクト(たとえば " NSNumber * whatToDoNumber = [NSNumber numberWithBool: doThis];")にして、1つのパラメーター@virataとして渡します。
Michael Dautermann、2012年

2
それは別の質問@Rajです...個別に投稿してください。
Michael Dautermann、

34

パラメーターを1つのオブジェクトにパッケージ化し、ヘルパーメソッドを使用して、Michaelなどが提案しているように、元のメソッドを呼び出すことができます。

別のオプションはdispatch_afterです。これは、ブロックを受け取り、特定の時間にそれをエンキューします。

double delayInSeconds = 15.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    [self fooFirstInput:first secondInput:second];

});

または、すでに発見したように、遅延を必要としない場合は、単に使用することができます - performSelector:withObject:withObject:


このアプローチの良い点は、使用__weakしてふりタイマーに自己への弱いリンクのみを与えることができることです。つまり、オブジェクトのライフサイクルを人為的に拡張することにはなりません。たとえば、performSelector:afterDelay:がテールのように影響する場合再帰(ただし、再帰はありません)は、保持サイクルを解決します。
トミー

1
はい、これは受け入れられる答えになるはずです。より適切で簡単です。
Roohul 2017

7

最も簡単なオプションは、NSArrayor などの両方の引数を含む単一のパラメーターを取るようにメソッドを変更することですNSDictionary(または、単一のパラメーターを受け取り、それをアンパックして最初のメソッドを呼び出し、次に2番目のメソッドを呼び出す2番目のメソッドを追加しますディレイ)。

たとえば、次のようなものが考えられます。

- (void) fooOneInput:(NSDictionary*) params {
    NSString* param1 = [params objectForKey:@"firstParam"];
    NSString* param2 = [params objectForKey:@"secondParam"];
    [self fooFirstInput:param1 secondInput:param2];
}

それを呼び出すには、次のようにします。

[self performSelector:@selector(fooOneInput:) 
      withObject:[NSDictionary dictionaryWithObjectsAndKeys: @"first", @"firstParam", @"second", @"secondParam", nil] 
      afterDelay:15.0];

メソッドを変更できない場合、UIKitなどにあるとしましょう。それだけでなく、使用するメソッドを変更すると、NSDictionaryタイプセーフも失われます。理想的ではありません。
fatuhoku 2015年

@fatuhoku-括弧で覆われています。「単一のパラメーターを取り、それをアンパックし、最初のメソッドを呼び出す2番目のメソッドを追加します」。これは、最初の方法がどこにあるかに関係なく機能します。型の安全性に関しては、それを使用することを決定した瞬間に失われましたperformSelector:(またはNSInvocation)。それが問題である場合、おそらく最良のオプションはGCDを通過することです。
2015年

6
- (void) callFooWithArray: (NSArray *) inputArray
{
    [self fooFirstInput: [inputArray objectAtIndex:0] secondInput: [inputArray objectAtIndex:1]];
}


- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second
{

}

そしてそれを呼び出す:

[self performSelector:@selector(callFooWithArray) withObject:[NSArray arrayWithObjects:@"first", @"second", nil] afterDelay:15.0];

5

提供されているすべてのタイプのperformSelector:メソッドをここで見つけることができます。

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html

バリエーションはたくさんありますが、複数のオブジェクトと遅延を使用するバージョンはありません。代わりに、引数をNSArrayまたはNSDictionaryでラップする必要があります。

- performSelector:
- performSelector:withObject:
- performSelector:withObject:withObject:
 performSelector:withObject:afterDelay:
 performSelector:withObject:afterDelay:inModes:
 performSelectorOnMainThread:withObject:waitUntilDone:
 performSelectorOnMainThread:withObject:waitUntilDone:modes:
 performSelector:onThread:withObject:waitUntilDone:
 performSelector:onThread:withObject:waitUntilDone:modes:
 performSelectorInBackground:withObject: 

2

NSInvocationの方法が複雑すぎて嫌いです。シンプルでクリーンな状態を保ちましょう:

// Assume we have these variables
id target, SEL aSelector, id parameter1, id parameter2;

// Get the method IMP, method is a function pointer here.
id (*method)(id, SEL, id, id) = (void *)[target methodForSelector:aSelector];

// IMP is just a C function, so we can call it directly.
id returnValue = method(target, aSelector, parameter1, parameter2);

いいね!「vc」を「target」に置き換えます
Anton

1

私はいくつかのスウィズリングを行っただけで、元のメソッドを呼び出す必要がありました。私がしたことは、プロトコルを作成し、それにオブジェクトをキャストすることでした。別の方法は、カテゴリ内でメソッドを定義することですが、警告を抑制する必要があります(#pragma clang診断は「-Wincomplete-implementation」を無視しました)。


0

シンプルで再利用可能な方法は、拡張NSObjectして実装することです

- (void)performSelector:(SEL)aSelector withObjects:(NSArray *)arguments;

何かのようなもの:

- (void)performSelector:(SEL)aSelector withObjects:(NSArray *)arguments
{
    NSMethodSignature *signature = [self methodSignatureForSelector: aSelector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: signature];
    [invocation setSelector: aSelector];

    int index = 2; //0 and 1 reserved
    for (NSObject *argument in arguments) {
        [invocation setArgument: &argument atIndex: index];
        index ++;
    }
    [invocation invokeWithTarget: self];
}

0

すべてのパラメーターをプロパティとして保持するカスタムオブジェクトを作成し、その単一のオブジェクトをパラメーターとして使用します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.