Introduction

In Objective-C, protocols and delegates are fundamental concepts that enable communication between objects. They are essential for creating flexible and reusable code, especially in the context of event-driven programming and user interface development.

Key Concepts

  1. Protocols: Define a list of methods that a class can implement. They are similar to interfaces in other programming languages.
  2. Delegates: A design pattern where one object acts on behalf of, or in coordination with, another object.

Protocols

Defining a Protocol

A protocol is defined using the @protocol keyword. It can include both required and optional methods.

@protocol MyProtocol <NSObject>
@required
- (void)requiredMethod;

@optional
- (void)optionalMethod;
@end

Implementing a Protocol

A class adopts a protocol by declaring it in the class interface and implementing the required methods.

@interface MyClass : NSObject <MyProtocol>
@end

@implementation MyClass
- (void)requiredMethod {
    NSLog(@"Required method implemented");
}

- (void)optionalMethod {
    NSLog(@"Optional method implemented");
}
@end

Practical Example

Let's create a simple protocol and a class that adopts it.

@protocol PrinterDelegate <NSObject>
@required
- (void)printDocument;
@end

@interface Printer : NSObject <PrinterDelegate>
@end

@implementation Printer
- (void)printDocument {
    NSLog(@"Printing document...");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Printer *printer = [[Printer alloc] init];
        [printer printDocument];
    }
    return 0;
}

Delegates

The Delegate Pattern

The delegate pattern allows an object to communicate back to its owner in a decoupled way. This is commonly used in UIKit for handling user interactions.

Setting Up a Delegate

  1. Define a Protocol: Define the methods that the delegate will implement.
  2. Declare a Delegate Property: In the class that will use the delegate, declare a property for the delegate.
  3. Implement the Delegate Methods: In the delegate class, implement the methods defined in the protocol.

Example: Using Delegates

Step 1: Define the Protocol

@protocol DownloadDelegate <NSObject>
- (void)downloadDidFinish;
@end

Step 2: Declare a Delegate Property

@interface Downloader : NSObject
@property (nonatomic, weak) id<DownloadDelegate> delegate;
- (void)startDownload;
@end

@implementation Downloader
- (void)startDownload {
    // Simulate a download process
    NSLog(@"Downloading...");
    [NSThread sleepForTimeInterval:2];
    NSLog(@"Download finished");
    
    // Notify the delegate
    if ([self.delegate respondsToSelector:@selector(downloadDidFinish)]) {
        [self.delegate downloadDidFinish];
    }
}
@end

Step 3: Implement the Delegate Methods

@interface ViewController : UIViewController <DownloadDelegate>
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    Downloader *downloader = [[Downloader alloc] init];
    downloader.delegate = self;
    [downloader startDownload];
}

- (void)downloadDidFinish {
    NSLog(@"Delegate received downloadDidFinish message");
}
@end

Exercises

Exercise 1: Create a Protocol and Implement It

  1. Define a protocol named CalculatorDelegate with a required method - (int)addNumber:(int)a toNumber:(int)b.
  2. Create a class Calculator that adopts this protocol and implements the method.
  3. In the main function, create an instance of Calculator and call the addNumber:toNumber: method.

Solution

@protocol CalculatorDelegate <NSObject>
@required
- (int)addNumber:(int)a toNumber:(int)b;
@end

@interface Calculator : NSObject <CalculatorDelegate>
@end

@implementation Calculator
- (int)addNumber:(int)a toNumber:(int)b {
    return a + b;
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Calculator *calculator = [[Calculator alloc] init];
        int result = [calculator addNumber:5 toNumber:3];
        NSLog(@"Result: %d", result);
    }
    return 0;
}

Exercise 2: Implement a Delegate Pattern

  1. Define a protocol named TaskDelegate with a method - (void)taskDidComplete.
  2. Create a class Task with a delegate property and a method - (void)performTask.
  3. Implement the delegate method in a class TaskHandler and set it as the delegate of Task.

Solution

@protocol TaskDelegate <NSObject>
- (void)taskDidComplete;
@end

@interface Task : NSObject
@property (nonatomic, weak) id<TaskDelegate> delegate;
- (void)performTask;
@end

@implementation Task
- (void)performTask {
    NSLog(@"Performing task...");
    [NSThread sleepForTimeInterval:2];
    NSLog(@"Task completed");
    
    if ([self.delegate respondsToSelector:@selector(taskDidComplete)]) {
        [self.delegate taskDidComplete];
    }
}
@end

@interface TaskHandler : NSObject <TaskDelegate>
@end

@implementation TaskHandler
- (void)taskDidComplete {
    NSLog(@"Delegate received taskDidComplete message");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Task *task = [[Task alloc] init];
        TaskHandler *handler = [[TaskHandler alloc] init];
        task.delegate = handler;
        [task performTask];
    }
    return 0;
}

Conclusion

In this section, we covered the basics of protocols and delegates in Objective-C. We learned how to define and implement protocols, and how to use the delegate pattern to enable communication between objects. These concepts are crucial for building modular and maintainable code in Objective-C. In the next module, we will delve into more advanced topics, such as categories and extensions.

© Copyright 2024. All rights reserved