I know how delegates work, and I know how I can use them.
But how do I create them?
When using the formal protocol method for creating delegate support, I've found that you can ensure proper type checking (albeit, runtime, not compile time) by adding something like:
if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
[NSException raise:@"MyDelegate Exception"
format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}
in your delegate accessor (setDelegate) code. This helps minimize mistakes.
Maybe this is more along the lines of what you are missing:
If you are coming from a C++ like viewpoint, delegates takes a little getting used to - but basically 'they just work'.
The way it works is that you set some object that you wrote as the delegate to NSWindow, but your object only has implementations (methods) for one or a few of the many possible delegate methods. So something happens, and NSWindow
wants to call your object - it just uses Objective-c's respondsToSelector
method to determine if your object wants that method called, and then calls it. This is how objective-c works - methods are looked up on demand.
It is totally trivial to do this with your own objects, there is nothing special going on, you could for instance have an NSArray
of 27 objects, all different kinds of objects, only 18 some of them having the method -(void)setToBue;
The other 9 don't. So to call setToBlue
on all of 18 that need it done, something like this:
for (id anObject in myArray)
{
if ([anObject respondsToSelector:@selector(@"setToBlue")])
[anObject setToBlue];
}
The other thing about delegates is that they are not retained, so you always have to set the delegate to nil
in your MyClass dealloc
method.
Here is a simple method to create delegates
Create Protocol in .h file. Make sure that is defined before the protocol using @class followed by the name of the UIViewController < As the protocol I am going to use is UIViewController class>.
Step : 1 : Create a new class Protocol named "YourViewController" which will be the subclass of UIViewController class and assign this class to the second ViewController.
Step : 2 : Go to the "YourViewController" file and modify it as below:
#import <UIKit/UIkit.h>
@class YourViewController;
@protocol YourViewController Delegate <NSObject>
@optional
-(void)defineDelegateMethodName: (YourViewController *) controller;
@required
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller;
@end
@interface YourViewController : UIViewController
//Since the property for the protocol could be of any class, then it will be marked as a type of id.
@property (nonatomic, weak) id< YourViewController Delegate> delegate;
@end
The methods defined in the protocol behavior can be controlled with @optional and @required as part of the protocol definition.
Step : 3 : Implementation of Delegate
#import "delegate.h"
@interface YourDelegateUser ()
<YourViewControllerDelegate>
@end
@implementation YourDelegateUser
- (void) variousFoo {
YourViewController *controller = [[YourViewController alloc] init];
controller.delegate = self;
}
-(void)defineDelegateMethodName: (YourViewController *) controller {
// handle the delegate being called here
}
-(BOOL)delegateMethodReturningBool: (YourViewController *) controller {
// handle the delegate being called here
return YES;
}
@end
//test whether the method has been defined before you call it
- (void) someMethodToCallDelegate {
if ([[self delegate] respondsToSelector:@selector(defineDelegateMethodName:)]) {
[self.delegate delegateMethodName:self];
}
}
An Objective-C delegate is an object that has been assigned to the delegate
property another object. To create one, you define a class that implements the delegate methods you're interested in, and mark that class as implementing the delegate protocol.
For example, suppose you have a UIWebView
. If you'd like to implement its delegate's webViewDidStartLoad: method, you could create a class like this:
@interface MyClass<UIWebViewDelegate>
// ...
@end
@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView {
// ...
}
@end
Then you could create an instance of MyClass and assign it as the web view's delegate:
MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;
On the UIWebView
side, it probably has code similar to this to see if the delegate responds to the webViewDidStartLoad:
message using respondsToSelector: and send it if appropriate.
if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
The delegate property itself is typically declared weak
(in ARC) or assign
(pre-ARC) to avoid retain loops, since the delegate of an object often holds a strong reference to that object. (For example, a view controller is often the delegate of a view it contains.)
To define your own delegates, you'll have to declare their methods somewhere, as discussed in the Apple Docs on protocols. You usually declare a formal protocol. The declaration, paraphrased from UIWebView.h, would look like this:
@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end
This is analogous to an interface or abstract base class, as it creates a special type for your delegate, UIWebViewDelegate
in this case. Delegate implementors would have to adopt this protocol:
@interface MyClass <UIWebViewDelegate>
// ...
@end
And then implement the methods in the protocol. For methods declared in the protocol as @optional
(like most delegate methods), you need to check with -respondsToSelector:
before calling a particular method on it.
Delegate methods are typically named starting with the delegating class name, and take the delegating object as the first parameter. They also often use a will-, should-, or did- form. So, webViewDidStartLoad:
(first parameter is the web view) rather than loadStarted
(taking no parameters) for example.
Instead of checking whether a delegate responds to a selector every time we want to message it, you can cache that information when delegates are set. One very clean way to do this is to use a bitfield, as follows:
@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end
@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end
@implementation Something {
struct {
unsigned int didFinishLoadingItem:1;
unsigned int didFailWithError:1;
} delegateRespondsTo;
}
@synthesize delegate;
- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
if (delegate != aDelegate) {
delegate = aDelegate;
delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
}
}
@end
Then, in the body, we can check that our delegate handles messages by accessing our delegateRespondsTo
struct, rather than by sending -respondsToSelector:
over and over again.
Before protocols existed, it was common to use a category on NSObject
to declare the methods a delegate could implement. For example, CALayer
still does this:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
This tells the compiler that any object might implement displayLayer:
.
You would then use the same -respondsToSelector:
approach as described above to call this method. Delegates implement this method and assign the delegate
property, and that's it (there's no declaring you conform to a protocol). This method is common in Apple's libraries, but new code should use the more modern protocol approach above, since this approach pollutes NSObject
(which makes autocomplete less useful) and makes it hard for the compiler to warn you about typos and similar errors.
Delegate :- Create
@protocol addToCartDelegate <NSObject>
-(void)addToCartAction:(ItemsModel *)itemsModel isAdded:(BOOL)added;
@end
Send and please assign delegate to view you are sending data
[self.delegate addToCartAction:itemsModel isAdded:YES];
As a good practice recommended by Apple, it's good for the delegate (which is a protocol, by definition), to conform to NSObject
protocol.
@protocol MyDelegate <NSObject>
...
@end
& to create optional methods within your delegate (i.e. methods which need not necessarily be implemented), you can use the @optional
annotation like this :
@protocol MyDelegate <NSObject>
...
...
// Declaration for Methods that 'must' be implemented'
...
...
@optional
...
// Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
...
@end
So when using methods that you have specified as optional, you need to (in your class) check with respondsToSelector
if the view (that is conforming to your delegate) has actually implemented your optional method(s) or not.