Open In App

What are the Protocols in Objective-C?

Last Updated : 14 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Objective-C is a language versatile and sophisticated enough for iOS and Mac OS GUI programming. One Objective-C feature that can be called both flexible and dynamic is the use of protocols. Protocols help in the creation of interfaces where a class implementation needs to follow a set of methods and properties.

What are the Protocols in Objective-C?

A protocol is a normative approach to constrain a set of classes that may require a common interface, for instance, delegates, data sources, and observers.

  1. This approach can also integrate secondary, or multiple, inheritance which is not permitted when working with Cocoa classes.
  2. A single class can be responsible for multiple protocols, which means it can easily adapt the behavior of several other classes without necessarily subclassing these classes.

Types of Protocols in Objective-C

There are two types of protocols in Objective-C, formal and informal protocols.

Formal Protocols

In the formal protocol declaration, the name of the protocol and its methods and properties are used after the prefix @protocol. Protocols declare methods and properties with modifiers, like @required or @optional, to indicate the needed implementation by the classes that accept the protocol. After this, the @end statement must appear. It marks the end of the protocol declaration.

For instance, the below code defines an arbitrary protocol called PrintProtocolDelegate, specifically, in which it declares a single necessary method called processCompleted.

ObjectiveC
@protocol PrintProtocolDelegate
@required
- (void)processCompleted;
@end

A class can accept a formal protocol by specifying the protocol name in angle brackets after the class name in the interface declaration. For example, the following code defines a class named SampleClass that accepts the PrintProtocolDelegate protocol.

ObjectiveC
@interface SampleClass : NSObject <PrintProtocolDelegate>
// class methods and properties
@end

When accepted, a class commits a contract to abide by the introduced methods and properties in the protocol. If a class does not behave according to an interface that a certain method or property says it must, the compiler will provide a warning message. This class does internal magic by creating the ability to choose from the mentioned methods and properties, purely based on the functionality desired.

We can declare either a variable or a parameter and assign it an object of any class that incorporates the protocol to a protocol type using the protocol. Saying that the code below defines a class referred to as PrintClass with a property set as the delegate of the type id<PrintProtocolDelegate>. This, therefore, means that the delegate property can hold a reference to any type that is an object of the PrintProtocolDelegate protocol.

ObjectiveC
@interface PrintClass : NSObject
@property (nonatomic, weak) id<PrintProtocolDelegate> delegate;
// class methods and properties
@end

The PrintClass can then use the delegate property to invoke the methods and properties defined by the protocol. For example, the following code shows how PrintClass can call the processCompleted method on its delegate object after printing some details.

ObjectiveC
@implementation PrintClass
- (void)printDetails {
    NSLog(@"Printing Details");
    [self.delegate processCompleted];
}
@end

The delegate property can be set to any object that implements the PrintProtocolDelegate protocol. For example, the following code shows how the SampleClass can set itself as the delegate of a PrintClass object and implement the processCompleted method.

ObjectiveC
#import <Foundation/Foundation.h>

@protocol PrintClassDelegate <NSObject>
- (void)processCompleted;
@end

@interface PrintClass : NSObject
@property (nonatomic, weak) id<PrintClassDelegate> delegate;
- (void)printDetails;
@end

@implementation PrintClass
- (void)printDetails {
    // Add your print details logic here
    [self.delegate processCompleted];
}
@end

@interface SampleClass : NSObject <PrintClassDelegate>
- (void)startAction;
@end

@implementation SampleClass
- (void)startAction {
    PrintClass *printClass = [[PrintClass alloc] init];
    printClass.delegate = self;
    [printClass printDetails];
}

- (void)processCompleted {
    NSLog(@"Printing Process Completed");
}
@end

int main() {
    SampleClass *sampleClass = [[SampleClass alloc] init];
    [sampleClass startAction];
    return 0;
}

Output:

1

Informal Protocols

That is not to say that informal protocols are not declared, just that the declaration of informal protocols employs the category of the NSObject class in place of @protocol.

  1. “Categories are a way of adding methods to the existing class without its subclassing” – groups methods that don’t add new behaviors to the original class.
  2. A non-official protocol is a list of rules that are not required by classes to implement, but they are expected to be available while the classes are under execution.

As an instance, the MyProtocol protocol has been formally declared in a category of the NSObject class, which is not as strict from an informal protocol standpoint. The protocol declares two methods: doSomething and doSomethingElse, on the other hand, which involves the organization of citizen groups for individual and collective actions.

ObjectiveC
@interface NSObject (MyProtocol)
- (void)doSomething;
- (void)doSomethingElse;
@end

A class can accept an informal protocol by implementing the methods defined by the protocol in its interface or implementation. This has no protocol name specified in the class declaration For instance, the following code shows the MyClass class that uses the MyProtocol unwritten protocol by implementing the doSomething method:

ObjectiveC
@interface MyClass : NSObject
// class methods and properties
@end
@implementation MyClass
- (void)doSomething {
    NSLog(@"Doing something");
}
@end

To have an informal protocol, we will declare a variable or a parameter of the type id and then assign it an object of any class that may accept the protocol. For example, the below code defines a function callProtocolMethod which accepts parameters of the type id as input and calls the method doSomething to the parameter:

ObjectiveC
void callProtocolMethod(id object) {
    [object doSomething];
}

The object parameter can be an object of anything that lists the `doSomething` method. In another case, the callProtocolMethod function might be used as shown in the code below when the class MyClass object is being passed.

ObjectiveC
int main(int argc, const char * argv[]) {
    MyClass *myObject = [[MyClass alloc] init];
    callProtocolMethod(myObject);
    return 0;
}

Usually, informal protocols have faced a deficit of popularity to official protocols and they are only used for continuation with the previous versions of Objective-C. They have some drawbacks, such as

  1. They are however checked by the compiler, therefore programs may contain the methods even though the particular classes that are accepting them do not implement them at all.
  2. They don’t correspond to the function declarations as returned by the header files so they are substantially more difficult to identify and apply.
  3. These collisions result from the repeated use of names that are the same but possess various signatures or connotations.

From this, a suggestion is drawn that a formal protocol should be used as it will largely fit more to safety, clarity, and consistency.

Syntax and Keywords

The following table summarizes the syntax and keywords related to protocols in Objective-C:

Syntax/Keyword

Description

@protocol ProtocolName

Starts the declaration of a formal protocol with the given name.

@end

Ends the declaration of a protocol or a class.

@required

Marks the following methods and properties as required to be implemented by the classes that accept the protocol. This is the default modifier if none is specified.

@optional

Marks the following methods and properties as optional to be implemented by the classes that accept the protocol.

<ProtocolName>

Specifies that a class or a variable conforms to the protocol with the given name.

id<ProtocolName>

Declares a variable or a parameter of the type ID that conforms to the protocol with the given name.

@protocol(ProtocolName)

Returns the Protocol object that represents the protocol with the given name.

conformsToProtocol:

Returns a Boolean value that indicates whether an object conforms to a given protocol.

respondsToSelector:

Returns a Boolean value that indicates whether an object responds to a given selector.

Examples

Here are examples of Objective-C protocols and their application as well as associated advantages.

Example 1: NSCopying Protocol

The NSCopying protocol is, by definition, a formal protocol that describes how one should do copying for an object. The protocol declares a single required method: copyWithZone:. This agreement has to be corrected by any class that is for copying and this this particular method is to be well executed.

For example, the following code defines a class named Person that accepts the NSCopying protocol and implements the copyWithZone: the solution. The method thereby inverts a new Person object that has the same name and age as the original object, assigning it to a new Person object and returning it.

ObjectiveC
@interface Person : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation Person
- (id)copyWithZone:(NSZone *)zone {
    Person *copy = [[Person allocWithZone:zone] init];
    copy.name = self.name;
    copy.age = self.age;
    return copy;
}
@end

The Person class can be used with methods and classes that have a requirement that the objects be copyable, like NSArray, NSDictionary, or NSKeyedArchiver, and the NSCopying protocol can be used for this purpose. For another example, the following code depicts the operation of copying the Person class to an array and adding the Person class by the following methods.

The Person class can be copied and used as a means of storage for later use.

ObjectiveC
int main(int argc, const char * argv[]) {
    Person *person1 = [[Person alloc] init];
    person1.name = @"Alice";
    person1.age = 25;
    
    Person *person2 = [person1 copy]; // create a copy of person1
    person2.name = @"Bob"; // change the name of person2
    
    NSArray *array = @[person1, person2]; // add both objects to an array
    
    for (Person *person in array) {
        NSLog(@"Name: %@, Age: %ld", person.name, person.age);
    }
    
    return 0;
}

Output:

2

This output shows that the person1 and person2 objects are different and independent copies, as changing the name of person2 does not affect the name of person1.

Example 2: NSCoding Protocol

The NSCoding protocol is a formal protocol that defines methods for encoding and decoding objects. The protocol declares two required methods: initWithCoder: and encodeWithCoder:. Any class that wants to support archiving and unarchiving must accept this protocol and implement these methods.

For example, the following code defines a class named Student that accepts the NSCoding protocol and implements the initWithCoder: and encodeWithCoder: methods. The methods encode and decode the name and grade properties of the Student object using the NSCoder object.

ObjectiveC
@interface Student : NSObject <NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger grade;
@end
@implementation Student
- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        self.name = [coder decodeObjectForKey:@"name"];
        self.grade = [coder decodeIntegerForKey:@"grade"];
    }
    return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.name forKey:@"name"];
    [coder encodeInteger:self.grade forKey:@"grade"];
}
@end

With several built-in protocols at one’s service, utilization of the NSCoding protocol enables Student class to be used with classes and methods that require codable objects, like NSKeyedArchiver, NSKeyedUnarchiver, or NSUserDefaults. Here is the code that shows that the student can be archived as unarchived using NSKeyedArchiver and NSKeyedUnarchiver classes:

ObjectiveC
int main(int argc, const char * argv[]) {
    Student *student1 = [[Student alloc] init];
    student1.name = @"Charlie";
    student1.grade = 10;
    
    // archive the student1 object to a file
    NSString *filePath = @"/Users/Desktop/student1.dat";
    BOOL success = [NSKeyedArchiver archiveRootObject:student1 toFile:filePath];
    if (success) {
        NSLog(@"Archived successfully");
    }
    
    // unarchive the student1 object from the file
    Student *student2 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    if (student2) {
        NSLog(@"Unarchived successfully");
        NSLog(@"Name: %@, Grade: %ld", student2.name, student2.grade);
    }
    
    return 0;
}

Output:
3

Through this, the personals are understood that the student1 as well as student2 objects are the same and have a common name and grade properties.

Example 3: Custom Protocol

Also, we can generate custom protocols that will be struggling to solve a particular need. For example, the following code defines a custom protocol named MathProtocol that declares two optional methods: multTwoNum: and addTwoNum. It indicates also the declaration of the result property that is required. Listen to the given audio and complete the sentence with the missing phrase.

ObjectiveC
@protocol MathProtocol
@optional
- (void)addTwoNumbers:(NSArray *)numbers;
- (void)multiplyTwoNumbers:(NSArray *)numbers;
@required
@property (nonatomic, assign) NSInteger result;
@end

The below code declares a class named Calculator which accepts the MathProtocol protocol and accords with the specified functions and properties. The operations involved here are simple and involve the entirety of the given numbers. Finally, as a result of the equations, a number is stored in its property.

ObjectiveC
@interface Calculator : NSObject <MathProtocol>
@property (nonatomic, assign) NSInteger result;
@end
@implementation Calculator
- (void)addTwoNumbers:(NSArray *)numbers {
    NSInteger sum = 0;
    for (NSNumber *number in numbers) {
        sum += [number integerValue];
    }
    self.result = sum;
}
- (void)multiplyTwoNumbers:(NSArray *)numbers {
    NSInteger product = 1;
    for (NSNumber *number in numbers) {
        product *= [number integerValue];
    }
    self.result = product;
}
@end

The text below describes the use of the Calculator class with the MathProtocol protocol which is given in the following code.

ObjectiveC
int main(int argc, const char * argv[]) {
    Calculator *calculator = [[Calculator alloc] init];
    
    // check if the calculator object responds to the addTwoNumbers: method
    if ([calculator respondsToSelector:@selector(addTwoNumbers:)]) {
        [calculator addTwoNumbers:@[@2, @3, @4]]; // call the method with an array of numbers
        NSLog(@"The sum is %ld", calculator.result); // print the result
    }
    
    // check if the calculator object responds to the multiplyTwoNumbers: method
    if ([calculator respondsToSelector:@selector(multiplyTwoNumbers:)]) {
        [calculator multiplyTwoNumbers:@[@2, @3, @4]]; // call the method with an array of numbers
        NSLog(@"The product is %ld", calculator.result); // print the result
    }
    
    return 0;
}

Output:

4

This means that the calculator object is compatible with the MathProtocol protocol and provides the default methods and the required properties.

Conclusion

Protocols is a beneficial low-level Objective-C feature that lets define a set of methods and properties that a class should implement. Inheritance protocols allow us to design a shared front end for some classes, to maintain several inheritances, and to provide copying, coding, and other functionalities subtly. The protocols can be informal/ formal and are mandatory/non-obligatory. Protocols may be created and utilized with correct keywords and syntax. As well, we will also be able to customize protocols to their matching our highly individual needs and specifications. Protocols serve as a useful instrument for making applications dynamic and flexible in Objective-C.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads