8

I have an NSOperation that wraps some web service functionality. The NSOperation has a delegate that is to be messaged when the operation is over.

As the NSOperation lives on a different thread I have to make the call like this:

[delegate performSelectorOnMainThread:@selector(getDealersIDSuccess:) withObject:result waitUntilDone:YES];

It works just fine, but it gives me a warning:

warning: '-performSelectorOnMainThread:withObject:waitUntilDone:' not found in protocol(s)

I completely agree with the compiler on this one, it sees a delegate, it checks the protocol, it finds no declaration of a performSelector method.

My question is: can I remove the warning by making this call in a different manner?

My two guesses are that I could (1) write a method called

- (void) callDelegateMethodOnMainThred {
    [delegate getDealersIDSuccess:result]
}

and call that through performSelectorOnMainThread, but I find that solution to be cumbersome and an extra, hard to read, step on top of the delegation.

The second solution could be to cast the delegate to the type of my parent object inside the selector, but that is just plain crazy and goes against the delegate encapsulation pattern.

I would really appreciate a third solution from someone with a better understanding of the language:)

Thank you in advance.

EDIT: Added delegate declaration:

id <ISDealersIDDelegate> delegate;

I declare my delegate as id. The delegate it self extends UIViewController.

I could see that declaring it NSObject would work.

RickiG
  • 11,380
  • 13
  • 81
  • 120

4 Answers4

18

performSelectorOnMainThread:withObject:waitUntilDone: method is declared in NSObject class. If your delegate object inherits from NSObject you can declare it as

NSObject<MyDelegateProtocol> *delegate;

So compiler will know that delegate responds to NSObject's methods and won't issue a warning.

Vladimir
  • 170,431
  • 36
  • 387
  • 313
  • +1 for the correct answer. The `` protocol only declares `performSelector:` (and friends). You need to expressly require that your delegate is a `NSObject*` that implements your delegate protocol. – d11wtq Dec 21 '10 at 13:48
  • Thanks Vladimir, your answer works perfectly. At first I was hesitant to cast the delegate to an NSObject instead of a generic id. Then it dawned on me that all objects inherits from NSObject. – RickiG Dec 23 '10 at 12:28
  • It sucks that even if your custom protocol inherits from the NSObject protocol, you still get this warning if the delegate is declared as 'id' and not 'NSObject*'. Perhaps the method in question is defined as an instance method of the NSObject class, but NOT as an instance method of the NSObject protocol? – Nicolas Miari Apr 11 '12 at 10:38
3

It might be even a better solution not call performSelectorOnMainThread: on a delegate or other protocol implementation. Make it the responsibility of the delegate/receiver to determine if it needs to do things on the main thread.

[delegate performSelector:@selector(delegateAction:)
               withObject:actionData];

Delegate implementation

- (void)delegateAction:(NSData*)actionData
{
    [self performSelectorOnMainThread:@selector(updateUIForAction:)
                           withObject:actionData
                        waitUntilDone:NO];
}

- (void)updateUIForAction:(NSData*)actionData
{
    // Update your UI objects here
}

It might look like more code, but the responsibility is in the right place now

Maarten
  • 51
  • 3
2

Actually on iOS 4 I prefer using NSNotifications and Observers (with Blocks) to perform updates on the mainthread.

- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *))block

I wrote down my design here.

Community
  • 1
  • 1
Henrik P. Hessel
  • 36,243
  • 17
  • 80
  • 100
  • Thanks Henrik, I like your approach, I just had a complete web proxy build over the last couple of months so not to fond of leaving that approach just yet. – RickiG Dec 23 '10 at 15:42
2

1) Declare your delegate's protocol to extend the NSObject protocol in the .h file

@protocol YourDelegateProtocol <NSObject>

2) Cast to NSObject in your call

[(NSObject*)delegate performSelectorOnMainThread:@selector(getDealersIDSuccess:) withObject:result waitUntilDone:YES];

I definitely recommend number (1).

deanWombourne
  • 38,189
  • 13
  • 98
  • 110
  • This one I like, especially for the easy implementation and the readability:) – RickiG Dec 21 '10 at 19:18
  • 1
    Hi Dean, your answer seems to be correct, but the warning persisted after trying it out (No. 1). – RickiG Dec 23 '10 at 12:30
  • 1
    In that case, the NSObject protocol doesn't contain that method - it's only in the NSObject class itself. sorry! (2) should still work though (though I think I prefer the accepted answer though!) – deanWombourne Dec 30 '10 at 20:44
  • 1
    Yeah, makes sense, the NSObject protocol only defines performSelector methods, not performSelectorOnMainThread methods. I went with Vladimirs solution and the warning is gone, and I learned both why it was there and a bit more about NSObject. – RickiG Jan 03 '11 at 11:48