Tutorial :Call back style



Question:

I am writing an iPhone application which in numerous places needs to perform non HTTP or FTP networking of a very simple request response type.

I've wrapped all this up into a SimpleQuery class that integrates with the run loop.

SimpleQuery *lookup = [[SimpleQuery alloc] init];  [lookup setDelegate:self];  [lookup doQueryToHost:queryServer port:queryPort query:queryString ];     

As you can see the calling object sets itself as a delegate. When the results are complete it then calls a method on the delegate with the results.

[delegate simpleQueryResult:resultString simpleQuery:self];               

I am now in a position where I have a user of SimpleQuery that has two types of query so I need to extend SimpleQuery to support this.

I can think of two sensible ways of doing this.

Firstly passing a selector into doQueryString, or a seperate doQueryStringWithSelector.

[lookup doQueryToHost:queryServer port:queryPort query:queryString selector:@SEL ];   

Secondly passing a tag into doQueryString so that when the delegate is called it can query the tag, as the simpleQuery is passed, to find out what the results are for.

[lookup doQueryToHost:queryServer port:queryPort query:queryString withTag:tag ];  

I'm just wondering which is best from a coding style perspective, the first seems simpler but tagging seems more in keeping with the iPhone SDK and Interface Builder


Solution:1

An option which is used commonly in Apple's code (for example, in UIControl) is to provide both a target object and a selector. This works only if there is a single callback, and is more appropriate than a delegate in that case. (If there are multiple callbacks, then you'll probably have to go with a delegate and the tag approach.)

If you go this route, then you do away with the delegate altogether and instead have a method with a signature like this:

doQueryToHost:(id)queryServer port:(int)queryPort query:(NSString*)queryString target:(id)target action:(SEL)action  

Note that "action" is typically preferred over "selector" in methods arguments in this case. The query would simply call the selector on the target when done. This would allow your clients to have multiple selectors, and also multiple target objects; this can help clean up code because you don't need to shove everything into a single delegate object.

If you want to go with your tag route, you should call it "context", which is what Apple uses (for example, in addObserver:forKeyPath:options:context).


Solution:2

There's a third option that's a common pattern in the kits, which is to use @protocols.

For example:

@protocol QueryCompleteHandlerProtocol  - (void)queryType1Complete:(int)intStuff;  - (void)queryType2Complete:(float)floatStuff;  @end  

What this does is declare a set of method calls that an object adopting the protocol has to conform to (the compiler will actually enforce this).

So your SimpleQuery object will hold on to something like the delegate pointer, which you might declare like this among the ivars:

NSObject<QueryCompleteHandlerProtocol> *callback;  

What this tells the compiler is that callback is an object that descends from NSObject and adopts the QueryCompleteHandlerProtocol protocol. Sometimes you see this written as:

id<QueryCompleteHandlerProtocol> callback;  

When you want to call the callback there's nothing special about them, SimpleQuery's methods will just call:

[callback queryType1Complete:1];  [callback queryType2Complete:2.0];  

Finally you client for the procotol class will declare itself as adopting the protocol:

@interface MyClass : NSObject<QueryCompleteHandlerProtocol>  ...  @end  

And will set itself as the callback with some code like:

[lookup setCallback:self];  

This is where the compiler checks that MyClass conforms to QueryCompleteHandlerProtocol, meaning it has implemented queryType1Complete: and queryType2Complete:.


Solution:3

I'm not sure I understand the problem here. Can't SimpleQuery's user just set another delegate object for the second query, or branch on the simpleQuery: parameter? That's a basic part of the delegate pattern, just like having two UIActionSheets for one view controller.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »