[iOS] - protocol and entrustment

Role of the agreement:

Similar to the interface, it is used to define the specifications that multiple classes should follow, but it will not provide the implementation of these methods, and the implementation of methods is left to the class.

Abstract relationships between specifications, classes, and instances

As can be seen from the figure, the internal state data of the same class and the implementation details of various methods are exactly the same. A class is a concrete implementation. The protocol defines a specification. The protocol defines the specification that a batch of classes need to comply with. It does not care about the internal state data of these classes or the implementation details of the methods in these classes. It only stipulates that some methods must be provided in this batch of classes, and the classes that provide these methods can meet the actual needs.

The protocol does not provide any implementation. The protocol embodies the design philosophy of separation of specification and implementation. Separating the specification from the implementation is also the benefit of the protocol, which is a loosely coupled design.

Use categories to implement informal agreements

Category (catgory) can realize informal agreement. This category is based on NSObject and creates a category for NSObject. When creating a category, you can specify the method that should be added to the category.

When a class implements the category of NSObject, it needs to implement all methods under the category. This category defined based on NSObject can be regarded as an informal agreement.

For example:

Based on NSObject, define a category with the name of Eatable and the code is as follows
#import <Foundation/Foundation.h>

//Define Eatable category based on NSObject
@interface NSObject (Eatable)
- (void) taste;
@end

The above defines a taste: Method in the Eatable class of NSObject. Next, all subclasses that inherit NSObject class will automatically carry this method, and the subclasses of NSObject can decide whether to implement this method according to their needs.

If the Eatable class is used as an informal protocol, it is equivalent to defining a specification. Therefore, subclasses that comply with the protocol usually implement this method.

Then derive a subclass for NSObject (Eatable):
#import <Foundation/Foundation.h>
#import "NSObject+Eatable.h"

//Defines the interface part of the class
@interface FKApple : NSObject
@end
#import "FKApple.h"

//Provide implementation part for FKApple
@implementation FKApple
- (void) taste {
    NSLog(@"Apple is nutritious and tastes good!");
}
@end

As can be seen from the above program, although no method is defined in the interface part, it will not affect the function of this class at all, because it inherits NSObject (Eatable), as long as the taste method is implemented in the implementation part of FKApple class.

The following is the test code of FKApple class:
#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc, char* argv[]) {
    @autoreleasepool {
        FKApple* app = [[FKApple alloc] init];
        [app taste];
    }
    return 0;
}

The output result is:

Objective-C does not enforce the implementation of all methods in the protocol, but if the taste method in FKApple class is not implemented, and the informal protocol itself does not implement the method, the following error will be caused:

Definition of formal agreement

The basic syntax of the formal agreement is as follows:
@protocol Protocol name <Parent agreement 1, Parent Agreement 2>
{
   Zero to more method definitions...
}

The above syntax is described in detail as follows:

  • The protocol name and class name are named according to the same rules.
  • A protocol can have multiple direct parent protocols, but protocols can only inherit protocols, not classes.
  • The method defined in the protocol has only method signature, but no method implementation; The methods contained in the protocol can be either class methods or instance methods.

The protocol defines the common behavior specification of multiple classes. Therefore, all methods in the protocol are open access rights.

A protocol is defined below:
#import <Foundation/Foundation.h>

//Define protocol
@protocol Output
//Method of defining protocol
- (void) output;
- (void) addData: (NSString*) msg;
@end

The above defines an Output protocol in which two methods are defined. The protocol does not care about the implementation of the method, which is equivalent to defining an interface.

Then define a Productable agreement, which represents the specifications that all products need to comply with:
#import <Foundation/Foundation.h>

//Define protocol
@protocol Productable
//Method of defining protocol
- (NSDate*) getProduceTime;
@end
Next, define a printer protocol, which inherits the above two protocols at the same time:
#import <Foundation/Foundation.h>
#import "Output.h"
#import "Productable.h"

//Define the protocol and inherit the above two protocols
@protocol Printable <Output, Productable>
//Method of defining protocol
- (NSString*) PrintColor;
@end

Protocol inheritance is different from class inheritance. The protocol fully supports multiple inheritance, that is, a protocol can have multiple direct parent protocols. Similar to class inheritance, if a child protocol inherits a parent protocol, it will get all the methods defined in the parent protocol.

When a protocol inherits multiple parent protocols, multiple parent protocols are arranged in the middle of < >, and multiple protocol ports are separated by English commas (,).

Compliance (realization) agreement

In the interface part of the class definition, you can specify the parent class inherited by the class and the protocol to be followed. The syntax is as follows:
@interface Class name: Parent class <Agreement 1, Agreement 2...>

As can be seen from the above syntax format, a class can abide by multiple protocols at the same time.

Provide an implementation class for Printable protocol: Printer. The interface code of this implementation class is as follows:
#import <Foundation/Foundation.h>
#import "Printable.h"

//Define the interface part of the class, inherit NSObject and abide by Printable protocol
@interface Printer :  NSObject <Printable>
@end
The implementation code of Printer class is as follows:
#import "Printer.h"
#import "Printable.h"
#define MAX_CACHE_LINE 10

@implementation Printer
{
    NSString* printData[MAX_CACHE_LINE];
    int dataNum;
}
- (NSString*) PrintColor {
    return @"gules";
}
- (void) output {
    while (dataNum > 0) {
        NSLog(@"Printer use%@Print:%@", self.PrintColor, printData[0]);
        dataNum--;
        for (int i = 0; i < dataNum; i++) {
            printData[i] = printData[i + 1];
        }
    }
}
- (void) addData: (NSString*) msg {
    if (dataNum >= MAX_CACHE_LINE) {
        NSLog(@"The output queue is full, adding failed");
    } else {
        printData[dataNum++] = msg;
    }
}

- (NSDate*) getProduceTime {
    return [[NSDate alloc] init];
}
@end

The Printer class implements the Printable protocol and all the methods in the two parent protocols of Printable and Printable. If the implementation class does not implement the PrintColor method in the protocol, the compiler will prompt the following warning.

The code of the test procedure is as follows:
#import <Foundation/Foundation.h>
#import "Printer.h"

int main(int argc, char* argv[]) {
    @autoreleasepool {
        //Create Printer object
        Printer* printer = [[Printer alloc] init];
        //Call the method of the Printer object
        [printer addData:@"insane iOS handout"];
        [printer addData:@"insane XML handout"];
        [printer output];
        [printer addData:@"insane Android handout"];
        [printer addData:@"insane Ajax handout"];
        [printer output];
        //Create a Printer object and use it as Productable
        NSObject<Productable>* p = [[Printer alloc] init];
        //Call the method defined in the Productable protocol
        NSLog(@"%@", p.getProduceTime);
        //Create a Printer object and use it as Output
        id<Output> out = [[Printer alloc] init];
        //Call the method defined in the Output protocol
        [out addData:@"Sun WuKong"];
        [out addData:@"Zhu Bajie"];
        [out output];
    }
    return 0;
}

The output results are as follows:

The two lines of bold code in the program do not use Printer to define variables, but use protocol to define variables. Then these variables can only call the methods declared in the protocol, otherwise the compiler will prompt an error.

If the program needs to use protocols to define variables, there are two methods:
  • Nsobject < protocol 1, protocol 2... > * variable;
  • ID < protocol 1, protocol 2... > variable;
    For variables defined by the above syntax format, their compile time type is only the protocol type followed, so they can only call the methods defined in the protocol.
Differences between formal and informal agreements:
  • Informal protocols are implemented by creating classes for NSObject, while formal protocols are created directly using @ protocol.
  • Compliance with informal agreements is achieved by inheriting NSObject with specific categories; Following the formal agreement has a special Objective-C syntax.
  • Compliance with informal agreements does not require the implementation of all methods defined in the agreement; To comply with a formal agreement, all methods defined in the agreement must be implemented.
In order to make up for the lack of flexibility caused by all methods that must be implemented to comply with the formal agreement, OC added two keywords @ optional and @ required, which are used as follows:
  • @Optional: the methods declared after the keyword and before @ required or @ end are optional -- the implementation class can choose to implement these methods or not.
  • @Required: the methods declared after the keyword and before @ optional or @ end are required -- the implementation class must implement these methods. If these methods are not implemented, the compiler will prompt a warning@ Required is the default behavior.

For example, the following protocols are defined:

@protocol Output
//Method of defining protocol
@optional
- (void) output;
@required
- (void) addData: (NSString*) msg;
@end

Two methods are defined in the above protocol. The implementation class of the protocol can optionally implement the output method, but it must implement the addData: method, otherwise the compiler will prompt a warning.

Agreement and delegate

Definition of delegation:

It is the design pattern that one object specifies another object to handle some specific tasks. Generally speaking, it means that "one object" entrusts what it wants to do to "another object".

Among them, "one object" is called the delegator, and "another object" is called the delegatee, that is, the agent.

Relationship between principal and agent

As shown in the figure below, the entrusting party assigns the task to the agent in some way, and the relationship between the two is the agreement.

In the procedure: generally

The tasks to be done by the entrusting party include:
  • Define protocols and methods
  • Declare delegate variables
  • Set agent
  • Calling delegate methods through delegate variables
The agent needs to do the following:
  • Follow the agreement
  • Implementation delegate method

Keywords: objective-c

Added by rix on Tue, 01 Feb 2022 19:24:38 +0200