Search code examples
iosobjective-ccocoa-touchcocoacocoa-design-patterns

Delegate declaration dilemma


I'm confused - I cannot understand what is the delegate is for?

The Application Delegate which is created by default is understandable, but in some cases I've seen something like this:

@interface MyClass : UIViewController <UIScrollViewDelegate> {
    UIScrollView *scrollView;
    UIPageControl *pageControl;
    NSMutableArray *viewControllers;
    BOOL pageControlUsed;
}

//...

@end

What is the <UIScrollViewDelegate> for?

How does it work and why is it used?


Solution

  • <UIScrollViewDelegate> is saying that the class conforms to the UIScrollViewDelegate protocol.

    What this really means is that the class must implement all of the required methods defined within the UIScrollViewDelegate protocol. Simple as that.

    You can conform your class to multiple protocols if you like:

    @implementation MyClass : UIViewController <SomeProtocol, SomeOtherProtocol>
    

    The purpose of conforming a class to a protocol is to a) declare the type as a conformant of the protocol, so you can now categorize this type under id <SomeProtocol>, which is better for delegate objects that objects of this class may belong to, and b) It tells the compiler to not warn you that the implemented methods are not declared in the header file, because your class conforms to the protocol.

    Here's an example:

    Printable.h

    @protocol Printable
    
     - (void) print:(Printer *) printer;
    
    @end
    

    Document.h

    #import "Printable.h"
    @interface Document : NSObject <Printable> {
       //ivars omitted for brevity, there are sure to be many of these :)
    }
    @end
    

    Document.m

    @implementation Document
    
       //probably tons of code here..
    
    #pragma mark Printable methods
    
       - (void) print: (Printer *) printer {
           //do awesome print job stuff here...
       }
    
    @end
    

    You could then have multiple objects that conform to the Printable protocol, which could then be used as an instance variable in, say, a PrintJob object:

    @interface PrintJob : NSObject {
       id <Printable> target;
       Printer *printer;
    }
    
    @property (nonatomic, retain) id <Printable> target;
    
    - (id) initWithPrinter:(Printer *) print;
    - (void) start;
    
    @end
    
    @implementation PrintJob 
    
    @synthesize target; 
    
    - (id) initWithPrinter:(Printer *) print andTarget:(id<Printable>) targ {
       if((self = [super init])) {
          printer = print;
          self.target = targ;
       }
       return self;
    }
    
    - (void) start {
       [target print:printer]; //invoke print on the target, which we know conforms to Printable
    }
    
    - (void) dealloc {
       [target release];
       [super dealloc];
    }
    @end