iOS - Birden çok bağımsız değişken ve afterDelay ile bir performSelector nasıl uygulanır?


90

Ben bir iOS acemisiyim. Aşağıdaki gibi bir seçici yöntemim var -

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

}

Bunun gibi bir şey uygulamaya çalışıyorum -

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

Ama bu bana bir hata veriyor -

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

Neyi kaçırdığıma dair bir fikriniz var mı?

Yanıtlar:


143

Kişisel olarak, ihtiyaçlarınıza daha yakın bir çözümün NSInvocation'ın kullanılması olduğunu düşünüyorum.

Aşağıdakine benzer bir şey işi yapacak:

indexPath ve dataSource , aynı yöntemde tanımlanan iki örnek değişkenidir.

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
Kabul. Doğru cevap olmalı. Çok yardımcı çözüm. Özellikle benim durumumda, birden fazla argüman içeren yöntemin imzasının değiştirilmesine izin verilmiyor.
AbhijeetMishra

Bu harika bir çözüm gibi görünüyor. Bu teknikle çağrılan yöntemden bir dönüş değeri elde etmenin bir yolu var mı?
David Pettigrew

15
Bu teknikle gecikmeyi nasıl belirliyorsunuz?
death_au

4
@death_au, bunun yerine invokecall: [inv performSelector:@selector(invoke) withObject:nil afterDelay:1]; Bunun harika bir çözüm olduğuna katılıyorum. Herkese mutlu kodlama!
Maxim Chetrusca

2
Sohbete biraz geç kaldım ama bir sorum var. DropDownDelegate nedir?
Minestrone-Soup

97

Çünkü [NSObject performSelector:withObject:withObject:afterDelay:]yöntem diye bir şey yok .

Göndermek istediğiniz verileri tek bir Objective C nesnesine (örneğin bir NSArray, bir NSDictionary, bazı özel Objective C türü) kapsüllemeniz ve ardından bunu [NSObject performSelector:withObject:afterDelay:]iyi bilinen ve sevilen yöntemden geçirmeniz gerekir.

Örneğin:

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

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

AfterDelay parametresini kaldırırsam bir hata almam. Bu, afterDelay'in birden fazla param ile kullanılmasına izin verilmediği anlamına mı geliyor?
Suchi

1
bir hata almazsınız, ancak bahse girerim çalışma zamanında "seçici bulunamadı" istisnası alırsınız (ve gerçekleştirmeye çalıştığınız şey çağrılmaz) ... deneyin ve görün. :-)
Michael Dautermann

Bool türünü buradan nasıl geçiririm?
virata

Bunu bir Hedef C tarzı nesne yapın (ör. " NSNumber * whatToDoNumber = [NSNumber numberWithBool: doThis];") Ve @virata parametresi olarak iletin.
Michael Dautermann

2
bu ayrı bir soru @Raj ... lütfen ayrı olarak gönderin.
Michael Dautermann

34

Parametrelerinizi tek bir nesnede paketleyebilir ve Michael ve diğerlerinin önerdiği gibi orijinal yönteminizi çağırmak için yardımcı bir yöntem kullanabilirsiniz.

Diğer bir seçenek ise, bir blok alacak ve belirli bir zamanda onu sıraya koyacak olan dispatch_after'dır.

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];

});

Ya da daha önce keşfettiğiniz gibi, gecikmeye ihtiyacınız yoksa sadece kullanabilirsiniz - performSelector:withObject:withObject:


Bu yaklaşımın iyi yanı __weak, taklit zamanlayıcınıza yalnızca zayıf bir bağlantı vermek için kullanabilmenizdir - böylece nesnenizin yaşam döngüsünü yapay olarak uzatmazsınız ve örneğin, performSelector: afterDelay: kuyruk benzeri bir şeyi etkiler. özyineleme (özyineleme olmasa da) daha sonra tutma döngüsünü çözer.
Tommy

1
Evet, bu kabul edilen cevap olmalıdır. Daha uygun ve yalındır.
Roohul

7

En basit seçenek, bir NSArrayveya gibi her iki bağımsız değişkeni içeren tek bir parametre alacak şekilde yönteminizi değiştirmektir NSDictionary(veya tek bir parametre alan, onu paketten çıkaran ve ilk yöntemi çağıran ikinci bir yöntem ekleyin ve ardından ikinci yöntemi gecikme).

Örneğin, aşağıdaki gibi bir şeye sahip olabilirsiniz:

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

Ve sonra onu aramak için şunları yapabilirsiniz:

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

Ya yöntem değiştirilemezse, mesela UIKit'te ya da başka bir yerde yaşıyorsa? Sadece bu değil, kullanılacak yöntemi değiştirmek de NSDictionarytip güvenliğini kaybeder. Uygun değil.
fatuhoku

@fatuhoku - Bu, parantez içinde yer alır; "tek bir parametre alan, onu paketinden çıkaran ve ilk yöntemi çağıran ikinci bir yöntem ekleyin". Bu , ilk yöntemin nerede yaşadığına bakılmaksızın işe yarar . Tip güvenliğine gelince, performSelector:(veya NSInvocation) kullanımına karar verildiği anda kaybedildi . Bu bir endişe ise, muhtemelen en iyi seçenek GCD'den geçmek olacaktır.
aroth

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


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

}

ve şununla çağırın:

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

5

Sağlanan tüm performSelector yöntem türlerini burada bulabilirsiniz:

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

Bir sürü varyasyon var, ancak birden çok nesneyi ve gecikmeyi alan bir sürüm yok. Bunun yerine argümanlarınızı bir NSArray veya NSDictionary ile özetlemeniz gerekir.

- 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 yöntemini sevmiyorum, çok karmaşık. Basit ve temiz tutalım:

// 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);

Güzel! 'Vc'yi' hedef 'ile değiştirin
Anton

1

Biraz tereddüt ettim ve orijinal yöntemi aramam gerekiyordu. Yaptığım şey bir protokol yapmak ve ona nesnemi atmaktı. Başka bir yol, yöntemi bir kategoride tanımlamaktır, ancak bir uyarının bastırılması gerekir (#pragma clang diagnostiği yok sayılır "-Wincomplete-implement").


0

Basit ve yeniden kullanılabilir bir yol, genişletmek NSObjectve uygulamaktır

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

gibi bir şey:

- (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

Tüm parametrelerimi özellik olarak tutan özel bir nesne oluşturur ve ardından bu tek nesneyi parametre olarak kullanırdım

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.