2011-12-08 27 views
86

Tôi là người mới sử dụng iOS. Tôi có một phương pháp chọn như sau -iOS - Cách triển khai performSelector với nhiều đối số và với afterDelay?

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

} 

Tôi cố gắng để thực hiện một cái gì đó như thế này -

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

Nhưng điều đó mang lại cho tôi một lỗi nói -

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

Bất kỳ ý tưởng như những gì tôi đang mất tích?

Trả lời

131

Cá nhân, tôi nghĩ rằng giải pháp gần hơn với nhu cầu của bạn là sử dụng NSInvocation.

Cái gì đó như sau đây sẽ làm việc:

indexPathdataSource là hai biến Ví dụ quy định tại cùng một phương pháp.

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]; 
} 
+1

có. đây sẽ là câu trả lời đúng. – hyperspasm

+2

Đồng ý. Nó phải là câu trả lời đúng. Giải pháp rất hữu ích. Đặc biệt trong trường hợp của tôi, nơi nó không được phép thay đổi chữ ký của phương thức chứa nhiều đối số. – AbhijeetMishra

+0

Điều này trông giống như một giải pháp tuyệt vời. Có cách nào để có được một giá trị trả về từ phương thức được gọi với kỹ thuật này không? –

94

Vì không có phương thức nào như phương thức [NSObject performSelector:withObject:withObject:afterDelay:].

Bạn cần đóng gói dữ liệu bạn muốn gửi cùng vào một đối tượng C Mục tiêu duy nhất (ví dụ: NSArray, NSDictionary, một số loại Mục tiêu C tùy chỉnh) và sau đó vượt qua phương thức [NSObject performSelector:withObject:afterDelay:] được biết đến và yêu thích.

Ví dụ:

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

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

Tôi không gặp lỗi nếu tôi xóa thông số afterDelay. Điều đó có nghĩa là afterDelay không được phép sử dụng với nhiều hơn một param? – Suchi

+1

bạn không nhận được lỗi, nhưng tôi cá rằng bạn sẽ nhận được ngoại lệ "không tìm thấy" lúc chạy (và thứ bạn đang cố thực hiện sẽ không được gọi) ... thử và xem . :-) –

+0

Làm cách nào để chuyển loại Bool tại đây? – virata

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


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

} 

và gọi nó với:

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

Tùy chọn đơn giản là thay đổi phương pháp của bạn để có một tham số duy nhất chứa cả hai tham số, chẳng hạn như một NSArray hoặc NSDictionary (hoặc thêm phương thức thứ hai lấy một tham số, bỏ gói và gọi phương thức đầu tiên, sau đó gọi giây thứ hai phương thức chậm trễ).

Ví dụ, bạn có thể có một cái gì đó như:

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

Và sau đó gọi nó, bạn có thể làm:

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

Điều gì sẽ xảy ra nếu phương pháp này không thể sửa đổi, hãy nói rằng nó tồn tại trong UIKit hay gì đó? Không chỉ vậy, việc thay đổi phương thức sử dụng 'NSDictionary' cũng mất an toàn kiểu. Không lý tưởng. – fatuhoku

+0

@fatuhoku - Điều đó được bao hàm bởi parenthetical; msgstr "thêm phương thức thứ hai lấy một tham số đơn, bỏ gói nó và gọi phương thức đầu tiên" #:. Điều đó hoạt động _regardless_ trong đó phương thức đầu tiên tồn tại. Đối với loại an toàn, đã mất thời điểm quyết định được thực hiện để sử dụng 'performSelector:' (hoặc 'NSInvocation'). Nếu đó là một mối quan tâm, lựa chọn tốt nhất có lẽ sẽ là đi qua GCD. – aroth

5

Bạn có thể tìm thấy tất cả các loại cung cấp performSelector: phương pháp ở đây:

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

Có một loạt các biến thể nhưng không có phiên bản nào có nhiều đối tượng cũng như độ trễ. Bạn sẽ cần phải quấn lên các đối số của bạn trong một NSArray hoặc NSDictionary thay thế.

- 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: 
32

Bạn có thể gói thông số của mình vào một đối tượng và sử dụng phương thức trợ giúp để gọi phương thức gốc là Michael và những người khác hiện đã đề xuất.

Tùy chọn khác là dispatch_after, sẽ mất một khối và đưa vào một thời điểm nhất định.

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

}); 

Hoặc, như bạn đã phát hiện ra, nếu bạn không yêu cầu sự chậm trễ bạn chỉ có thể sử dụng - performSelector:withObject:withObject:

+0

Điều tốt về phương pháp này là bạn có thể sử dụng '__weak' để cung cấp cho bộ hẹn giờ giả của bạn chỉ một liên kết yếu trở lại bản thân - vì vậy bạn không kết thúc mở rộng vòng đời của đối tượng và ví dụ: nếu performSelector của bạn: afterDelay: hiệu ứng một cái gì đó một chút giống như đệ quy đuôi (mặc dù không có đệ quy) thì nó giải quyết chu trình giữ lại. – Tommy

+0

Đây phải là câu trả lời được chấp nhận. – user1105951

+0

Có phải đây là câu trả lời được chấp nhận. Nó thích hợp hơn và thẳng về phía trước. – Roohul

1

tôi chỉ làm một số swizzling và cần thiết để gọi phương thức gốc. Những gì tôi đã làm là tạo một giao thức và đưa đối tượng của tôi vào nó. Một cách khác là xác định phương thức trong một danh mục, nhưng sẽ cần phải loại bỏ một cảnh báo (#pragma clang diagnostic bị bỏ qua "-Wincomplete-implementation").

2

Tôi không thích cách NSInvocation quá phức tạp. Hãy đơn giản và sạch sẽ:

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

Đẹp! Thay thế 'vc' bằng 'mục tiêu' – Anton

Các vấn đề liên quan