Method signature for a Selector
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
开发者_如何学C-(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.
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];
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
}
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.
精彩评论