How to Create Delegates in Objective-C

How do I create delegates in Objective-C?

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.)

Making Delegates for Your Classes

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.

Naming

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.

Speed Optimizations

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.

Informal Delegates

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.

How to create my own delegate (user defined delegate in Objective-C)

First define a declare a delegate like this -

@protocol IconDownloaderDelegate;

Then create a delegate object like this -

@interface IconDownloader : NSObject
{
NSIndexPath *indexPathInTableView;
id <IconDownloaderDelegate> delegate;
NSMutableData *activeDownload;
NSURLConnection *imageConnection;
}

Declare a property for it -

@property (nonatomic, assign) id <IconDownloaderDelegate> delegate;

Define it -

@protocol IconDownloaderDelegate 

- (void)appImageDidLoad:(NSIndexPath *)indexPath;

@end

Then you can call methods on this delegate -

[delegate appImageDidLoad:self.indexPathInTableView];

Here is the complete source code of the image downloader class -

.h file -

@class AppRecord;
@class RootViewController;

@protocol IconDownloaderDelegate;

@interface IconDownloader : NSObject
{
AppRecord *appRecord;
NSIndexPath *indexPathInTableView;
id <IconDownloaderDelegate> delegate;

NSMutableData *activeDownload;
NSURLConnection *imageConnection;
}

@property (nonatomic, retain) AppRecord *appRecord;
@property (nonatomic, retain) NSIndexPath *indexPathInTableView;
@property (nonatomic, assign) id <IconDownloaderDelegate> delegate;

@property (nonatomic, retain) NSMutableData *activeDownload;
@property (nonatomic, retain) NSURLConnection *imageConnection;

- (void)startDownload;
- (void)cancelDownload;

@end

@protocol IconDownloaderDelegate

- (void)appImageDidLoad:(NSIndexPath *)indexPath;

@end

.m file -

#import "IconDownloader.h"
#import "MixtapeInfo.h"

#define kAppIconHeight 48
#define TMP NSTemporaryDirectory()

@implementation IconDownloader

@synthesize appRecord;
@synthesize indexPathInTableView;
@synthesize delegate;
@synthesize activeDownload;
@synthesize imageConnection;

#pragma mark

- (void)dealloc
{
[appRecord release];
[indexPathInTableView release];

[activeDownload release];

[imageConnection cancel];
[imageConnection release];

[super dealloc];
}

- (void)startDownload
{
self.activeDownload = [NSMutableData data];

// alloc+init and start an NSURLConnection; release on completion/failure
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:appRecord.mixtape_image]] delegate:self];
self.imageConnection = conn;
[conn release];

}

- (void)cancelDownload
{
[self.imageConnection cancel];
self.imageConnection = nil;
self.activeDownload = nil;
}


#pragma mark -
#pragma mark Download support (NSURLConnectionDelegate)

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.activeDownload appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Clear the activeDownload property to allow later attempts
self.activeDownload = nil;

// Release the connection now that it's finished
self.imageConnection = nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Set appIcon and clear temporary data/image
UIImage *image = [[UIImage alloc] initWithData:self.activeDownload];
self.appRecord.mixtape_image_obj = image;

self.activeDownload = nil;
[image release];

// Release the connection now that it's finished
self.imageConnection = nil;

// call our delegate and tell it that our icon is ready for display
[delegate appImageDidLoad:self.indexPathInTableView];
}

@end

and here is how we use it -

#import "IconDownloader.h"

@interface RootViewController : UITableViewController <UIScrollViewDelegate, IconDownloaderDelegate>
{
NSArray *entries; // the main data model for our UITableView
NSMutableDictionary *imageDownloadsInProgress; // the set of IconDownloader objects for each app
}

in .m file -

- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[IconDownloader alloc] init];
iconDownloader.appRecord = appRecord;
iconDownloader.indexPathInTableView = indexPath;
iconDownloader.delegate = self;
[imageDownloadsInProgress setObject:iconDownloader forKey:indexPath];
[iconDownloader startDownload];
[iconDownloader release];
}
}

here is delegate gets called automatically -

// called by our ImageDownloader when an icon is ready to be displayed
- (void)appImageDidLoad:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader != nil)
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:iconDownloader.indexPathInTableView];

// Display the newly loaded image
cell.imageView.image = iconDownloader.appRecord.appIcon;
}
}

How to declare events and delegates in Objective-C?

In the example where class A calls class B to perform an action and then return something we would do this:

Class B's .h, this goes above the @interface

@protocol CLASSBNAMEDelegate <NSObject>

- (void) YOURMETHOD:(id) returnValue

@end

Then under the @interface we add a delegate property:

@property (nonatomic, weak) id < CLASSBNAMEDelegate > delegate;

In class b .m where you want to send a message back to Class A you would:

 if ([self.delegate respondsToSelector:@selector(YOURMETHOD:)]) {
[self.delegate YOURMETHOD:value];
}

In Class A, where you use Class B be sure to set the delegate like so:

ClassB *b = [Class B etc....];
[b setDelegate:self];

IN Class A Header make sure you:

@interface CLASSA : NSObject <CLASSBNAMEDelegate>

Then you would need to respond to the selector:

- (void) YOURMETHOD:(id) value{}

Hope this helps...

Delegates in objective c

Delegates in Objective-C are merely a concept, not some kind of implementation artifact (like in C#). A delegate in Objective-C (better: Cocoa) is basically an object, which is notified by whoever uses it as its "delegate" of certain events occuring. Delegates may also be asked to perform certain tasks on behalf of the host object. The interface a delegate is required to implement is often formalized by a protocol.

@protocol ActionDelegate 

- (void) actionDidStart: (id) aSender;
- (void) actionDidEnd: (id) aSender;

@end

@interface Action: NSObject {
id<ActionDelegate> delegate;
}

@property (nonatomic,assign) id<ActionDelegate> delegate;

@end

Delegates in C#, on the other hand, are an implementation artifact. There is a dedicated delegate keyword to declare delegate types and to create actual delegate instances.

class Action {

delegate void ActionDidStartDelegate(Action sender);
delegate void ActionDidEndDelegate(Action sender);

...
}

(my C# is a bit rusty, so the syntax may be off here, sorry; and in real life, one would probably use events in situations like the above rather than raw delegates). Basically, a C# delegate is akin to a Python method object.

You might be able to use the new code block feature of Objective-C to emulate delegates. Not having used this feature (yet), I cannot comment on this. Another way to get something like that would be to use plain function pointers.

typedef void (*callback_function)();

- (void) doSomethingWithCallback: (callback_function) func {
...
func();
}

And of course, you can always use the method often employed by Cocoa itself: use an object and an associated method selector:

- (void) doSomethingWhenDonePerform: (SEL)aSelector onObject: (id) aReceiver {
...
[aReceiver perform: aSelector];
}


Related Topics



Leave a reply



Submit