views:

233

answers:

3

I'm trying to implement delegation for a class which should call it's delegate (if any), when special things happen.

From Wikipedia I have this code example:

 @implementation TCScrollView
 -(void)scrollToPoint:(NSPoint)to;
 {
   BOOL shouldScroll = YES;
   // If we have a delegate, and that delegate indeed does implement our delegate method,
   if(delegate && [delegate respondsToSelector:@selector(scrollView:shouldScrollToPoint:)])
     shouldScroll = [delegate scrollView:self shouldScrollToPoint:to]; // ask it if it's okay to scroll to this point.

   if(!shouldScroll) return;  // If not, ignore the scroll request.

   /// Scrolling code omitted.
 }
 @end

If I try this on my own, I get a warning that the method I am calling on the delegate was not found. Of course it was not, because the delegate is just referenced by id. It could be anything. Sure at runtime that will work fine because I check if it responds to selector. But I don't want the warning in Xcode. Are there better patterns?

+7  A: 

You could let the delegate be of the id type that implements the SomeClassDelegate protocol. For this, you could in the header of your SomeClass (in your case TCScrollView), do something like this:

@protocol TCSrollViewDelegate; // forward declaration of the protocol

@interface TCScrollViewDelegate {
    // ...
    id <TCScrollViewDelegate> delegate;
}
@property (assign) id<TCScrollViewDelegate> delegate;
@end

@protocol TScrollViewDelegate
- (BOOL) scrollView:(TCScrollView *)tcScrollView shouldScrollToPoint:(CGPoint)to;
@end

Then you can from your implementation, just call the method on the delegate:

@implementation TCScrollView

-(void)scrollToPoint:(NSPoint)to;
{
  BOOL shouldScroll = YES;
  shouldScroll = [delegate scrollView:self shouldScrollToPoint:to]; // ask it if it's okay to scroll to this point.
  if(!shouldScroll) return;  // If not, ignore the scroll request.
  /// Scrolling code omitted.
}
@end
drvdijk
A: 

Following up on the sample code in drvdijk's answer, there could be a problem if there is any chance that delegate could be nil when you call the delegate method.

The return value of a message sent to nil is nil (aka 0.0 aka 0 aka NO), so if delegate is nil,

[delegate scrollView:self shouldScrollToPoint:to]

will return NO, which might not be the desired behavior in your case. It's safer to check first:

if (delegate != nil) {
    shouldScroll = [delegate scrollView:self shouldScrollToPoint:to]
}


Also, if you don't want to see a compiler warning when sending messages declared by NSObject to your delegate (such as respondsToSelector:), include the NSObject protocol in your protocol declaration:

@protocol TScrollViewDelegate <NSObject>
- (BOOL) scrollView:(TCScrollView *)tcScrollView shouldScrollToPoint:(CGPoint)to;
@end
Ole Begemann
A: 

Use [NSObject performSelector:]

[delegate performSelector:@selector(scrollView:shouldScrollToPoint:) withObject:self withObject:to];

You won't get the compiler warnings anymore.

Alternatively create a prototcol and declare MyProtocol *delegate in header file.

Jason Harwig