An overview of the

Apple’s official explanation:

Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, Another object. The delegating object keeps a reference to The other object — The delegate — and at The appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object.

The general idea is that a proxy is a simple and powerful pattern in which a proxy holds a reference to a proxy object and sends a message to it, and the proxy receives the message and processes the return result. Its main value is that it can realize the function of implementing multiple objects in one object, similar to “multiple inheritance”.

The Proxy pattern is a common design pattern. It is widely used in Cocoa framework to separate data from UI, such as UITableView and UIApplicationDelegate. It consists of three parts:

  • An agreement specifying what both parties can and must do;
  • The agent, in accordance with the designated agreement, to complete the functions required by the principal;
  • The principal, according to the specified agreement, specifies the agent to perform what function;

The intuitive relationship diagram is as follows:

use

The following will focus on the concept and use of “agreement”;

Protocol agreement

Protocol is similar to an interface in Java or a pure virtual function in c++, which provides only the interface but not the implementation. Unlike a pure virtual function in c++, I does not have class inheritance but follows Protocol inheritance.

@protocol FSSubDelegate <NSObject, FSDelegate>

@required
//methodList or protertyList
@optional
//methodList or protertyList

@end
Copy the code

The protocol has two modifiers, @required and @optional, which default to @required. The modifiers indicate whether a method or property must be implemented to comply with the protocol.

The protocol needs to inherit the “base protocol” NSObject(as opposed to the NSObject base class), which specifies some basic methods and properties that all base classes need to implement, such as isEqual: , isKindOfClass, respondsToSelector, etc. Memory management methods retain, release, AutoRelease, retainCount, etc. This defines a relatively uniform interface and OC objects can respond to the method. OC objects do not support multiple inheritance, but protocols can.

Note: Attributes can be added to protocols, but are rarely used in proxy mode, but category categories cannot be added unless associated objects are used.

Common methods in protocols that inherit from the NSObject protocol are as follows:

- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
- (BOOL)respondsToSelector:(SEL)aSelector;
Copy the code

Since the protocol is “class independent”, any class can implement the defined protocol. Therefore, the conformsToProtocol: method can be used to determine whether a class implements a specific protocol. Even if @required is specified in the protocol, the proxy object does not comply with the protocol and does not implement methods that the protocol must implement (just compile warnings), and can use respondsToSelector to determine whether the proxy object implements a particular method, avoiding run-time crashes.

The location of the protocol definition can be determined according to the application situation. If the protocol is only used in a certain class, you can directly define it in the class file. If multiple classes use the same protocol, they can be defined in a unified file.

Proxy basic usage

Take open common UITableView as an example, the general use is as follows:

@protocol UITableViewDelegate <NSObject, UIScrollViewDelegate> ... @end @protocol UITableViewDataSource <NSObject> ... @end @interface UITableView @property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate; @end @implementation UIViewController <UITableViewDelegate, UITableViewDataSource> - (void) viewDidLoad { self.tableView.delegate = self; self.tableView.dataSource = self; } #pragma mark -- UITableViewDataSource //// #pragma mark -- UITableViewDataSource ////Copy the code

Diagram interpretation based on proxy mode, where the protocols for UITableViewDelegate and UITableViewDataSource are presented respectively

UITableVeiwCell display, edit, select and its content, index, number and other requirements; The delegate holds a weakly-referenced delegate for the UITableView, which calls methods in the protocol and passes parameters through the delegate. The agent UIViewController needs to implement the methods in the protocol to fulfill the requirements in UITableView, and finally realize the content display, edit, slide, jump and other actions of the UITableView control. The proxy attribute in the principal should be a weak reference to avoid cyclic reference with the proxy and the two objects cannot be released.

The above general example illustrates that a proxy can have multiple proxy objects, and a proxy object can have multiple proxies (for example, a datasource proxy object can be implemented by separating objects rather than specifying UIViewController to achieve loose coupling).

Principle of implementation

struct objc_class {Class isa OBJC_ISA_AVAILABILITY; .struct objc_protocol_list *protocols 	OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;

struct objc_protocol_list {
    struct objc_protocol_list * _Nullable next;
    long count;
    __unsafe_unretained Protocol * _Nullable list[1];
};

struct protocol_t : objc_object {
    const char *mangledName;
    struct protocol_list_t *protocols;
    method_list_t *instanceMethods;
    method_list_t *classMethods;
    method_list_t *optionalInstanceMethods;
    method_list_t *optionalClassMethods;
    property_list_t *instanceProperties;
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
    // Fields below this point are not always present on disk.
    const char **_extendedMethodTypes;
    const char *_demangledName;
    property_list_t *_classProperties;
		// Omit some encapsulated convenience get methods. };Copy the code

Observing the structure of the class object, it can be found that there is a protocol linked list of type struct objC_PROTOCOL_list, and there is attribute information in the protocol structure (also indicating that the protocol can define attributes), so the proxy object implements protocol methods, that is, adding protocol methods to the class object. You can obtain the protocol method through the following Runtime method: The specific class structure diagram is as follows:

method

Reference

Cocoa Core Competencies — Delegation

Do you really understand the iOS proxy design pattern?

IOS learning notes “fake” multiple inheritance

Objective – C: agent

Writing high quality Objective-C code for iOS (part 4) — Protocols and categories

IOS developer – Protocol to define attributes?

Foundation: NSObject Protocol