views:

351

answers:

3

Hi,

I'm new to the Objective C business (Java developer most of the time) and am woking on my first killer app now. :-) At the moment I am somehow confused about the usage of selectors as method arguments. They seem to be a little bit different than delegates in C# for example.

Given the following method signature

-(void)execute:(SEL)callback;

is there a way to enforce the signature for the selector passed to such a method? The method is expecting a selector of a method with the following signature

-(void)foo:(NSData*)data;

But the SEL (type) is generic, so there is a good chance to pass a wrong selector to the execute method. OK at least at runtime one would see a funny behavior... but I would like to see a compiler warning/error when this happens.

A: 

If you want to enforce the data handling, use isKindOfClass inside your selector. This works a lot like instanceof which you are familiar with in Java.

David Sowsy
Thanks, but this applies only at runtime. I would like to avoid such "errors" in code and let the compiler report it at compile time. Similar to protocols.id<SomeProtocol>Everything not applying to the protocol SomeProtocol results in compiler warnings. There is no such notation for selectors? SEL<SelectorSignatureDescription>Right?In Java I would specify a interface with the callback method signature (similar to Runnable for example) and pass an anonymous implementation to the method.In C# one can specify the signature of a delegate.But how to do it in objective c?
MacTouch
Unfortunately all of the good habits (trading runtime problems for compile time problems) that we've learned in Java are pretty much thrown out the window in Objective-C. It is a dynamic system. Protocols might help give you the polymorphism you want, but still not help you with type safety on selectors.
David Sowsy
+2  A: 

To directly answer your question, no, the SEL type allows any type of selector, not just ones with a specific signature.

You may want to consider passing an object instead of a SEL, and document that the passed object should respond to a particular message. For example:

- (void)execute:(id)object
{
    // Do the execute stuff, then...
    if ([object respondsToSelector:@selector(notifyOnExecute:)]) {
        [object notifyOnExecute:self];
    }
    // You could handle the "else" case here, if desired
}
mipadi
OK, thanks. The implementation is not optional.I think I do it using protocols and a kind identifiers for the "result" in callbacks.For example©protocol ActionCallback<NSObject>-(void)processData:(Action*)action data:(NSData*)data id:(NSUInteger)actionId;@endand let the implementation deal with the Action-Ids (in a switch statement).
MacTouch
+5  A: 

The quick answer is: no, there is no way to have the compiler enforce the method signature of a method selector that is provided via a SEL argument.

One of the strengths of Objective-C is that it is weakly-typed language, which allows for a lot more dynamic behaviour. Of course, this comes at the cost of compile-time type safety.

In order to do what (I think) you want, the best approach is to use delegates. Cocoa uses delegates to allow another class to implement "callback"-type methods. Here is how it might look:

FooController.h

@protocol FooControllerDelegate
@required:
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
@end

@interface FooController : NSObject
{
    id <FooControllerDelegate> * delegate;
}
@property (assign) id <FooControllerDelegate> * delegate;
- (void)doStuff;
@end

FooController.m

@interface FooController (delegateCalls)
- (void)handleData:(NSData *)data;
@end

@implementation FooController

@synthesize delegate;

- (id)init
{
    if ((self = [super init]) == nil) { return nil; }
    delegate = nil;
    ...
    return self;
}

- (void)doStuff
{
    ...
    [self handleData:data];
}

- (void)handleData:(NSData *)data
{
    if (delegate != nil)
    {
        [delegate handleData:data forFoo:self];
    }
    else
    {
        return;
        // or throw an error
        // or handle it yourself
    }
}

@end

Using the @required keyword in your delegate protocol will prevent you from assigning a delegate to a FooController that does not implement the method exactly as described in the protocol. Attempting to provide a delegate that does not match the @required protocol method will result in a compiler error.

Here is how you would create a delegate class to work with the above code:

@interface MyFooHandler <FooControllerDelegate> : NSObject
{
}
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
@end

@implementation MyFooHandler
- (void)handleData:(NSData *)data forFoo:(FooController *)foo
{
    // do something here
}
@end

And here is how you would use everything:

FooController * foo = [[FooController alloc] init];
MyFooHandler * fooHandler = [[MyFooHandler alloc] init];
...
[foo setDelegate:fooHandler]; // this would cause a compiler error if fooHandler
                              // did not implement the protocol properly
...
[foo doStuff]; // this will call the delegate method on fooHandler
...
[fooHandler release];
[foo release];
e.James
Thank you. This seems to be the way to go in objective c (I use this approach for retrieving results from Subviews). Is a common pattern in cocoa. Much code to just delegate the results of few asynchronous actions to specific methods. I don't like switch statements. They are a capitulation in face of OOP. ;)When there are many actions to handle one must have a god-method responding to all the action objects.
MacTouch
I share your sentiment about switch statements, but I'm not quite sure how it applies to Objective-C development. I have written a large desktop app in Cocoa, and managed to avoid the kind of non-OO programming that you describe. If you find yourself needing a large number of switch statements or god-methods, you may be approaching something from the wrong angle.
e.James
Yes you are right. I'm now rethinking what I do before things become complicated. ;)I have a abstract Action class and a couple of implementations derived from it. All the actions read data from different places in the "wild world web" and map the results to data objects with different types (Message, Person etc.)All actions are invoked within the same controller. The controller has to respond to each action in a different way. The idea was to delegate the results to different methods within the controller using a selector as argument to the init-Method of the action implementation.
MacTouch
Asking here is a great place to start. It is always good to see a developer who is new to the language and actively looking for advice about best practices. Too many developers in your shoes try to turn Objective-C into Java instead of learning how to use it the way it was intended. Good luck with your project `:)` it sounds like you are on the right track!
e.James
There are people who try to use "tools" not the way they are intended to be used and of course, they are not always wrong.You know the Nils Bohr barometer problem? Haha.http://vipmeister.com/story/skyscraber.html
MacTouch
Ha! I hadn't read that one before. Thanks for providing the link `:)`
e.James