Proxy message forwarding in practice

Time:2022-10-16

friendly reminder:Click to view the demo code involved in this article

Introduction

A proxy is a transaction on behalf of an authorized party (From Wikipedia).

Think about when we will use proxies in our life?

When renting or buying a house, we need an intermediary to help us contact the landlord, deal with the formalities, and reduce the cost of communication between us and the landlord.
When ordering takeout, we need the delivery guy to help us deliver it so that we can have more time to focus on other things.

So it can be understood that the intermediary helps us solve problems on two levels:

  • reduce interdependence issues
  • reduce repetitive tasks

So in essence, Proxy embodies the design idea of ​​”middle layer”, which is specifically applied to the business scenario of “message forwarding”.

circular reference

Before talking about today’s Demo, let’s recall the application scenarios of Proxy that we have contacted before. I think the first thing in your mind must be: use Proxy to solve the problem of NSTimer circular reference.

So let’s first talk about the “resolving circular reference” scenario that Proxy uses the most just needed.

How do circular references arise?

The following figure shows the process of normal memory recovery:

Proxy message forwarding in practice

The following is the process of generating a circular reference leading to a memory leak:

Proxy message forwarding in practice

The best way to verify whether a circular reference is generated is to determine whether a reference cycle is generated.

NSTimer circular reference problem

The most interesting point of the NSTimer problem is that 80% of the explanations on the Internet about why NSTImer cause circular references are unclear, such as this most common statement:

Proxy message forwarding in practice

Such a statement is like someone asking Xiao Ming: “Why does NSTimer cause a circular reference?”

Xiao Ming replied: “NSTimer will cause circular reference”.

It put on a good show of “waiting for it”.

The circular reference must be a strong reference between ViewController and NSTimer, but whyNSTimer addTargetwill cause circular references, but we usually useUIButton addTargetBut won’t cause circular reference?

Answering this question clearly can be regarded as clarifying “why does NSTimer cause circular references”.

In fact, it is very simple to answer this question. Let’s check the documentation provided by Big Apple.

forUIControl

The control does not retain the object in the target parameter. It is your responsibility to maintain a strong reference to the target object while it is attached to a control.

The translation into Chinese means: The control does not retain the object in the target parameter. It is your responsibility to maintain a strong reference to the target object when it is attached to the control

forNSTimerdescribed as follows:

The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated.

The Chinese meaning is: when the timer is triggered, the object to which the message specified by aSelector is to be sent. The timer maintains a strong reference to the object until it (the timer) expires.

Proxy message forwarding in practice

This will make it clearer. The reason why NSTimer will lead to strong references, but UIControl will not lead to strong references, is a feature of Big Apple, which achieves a real analog in-depth effect.

We are all proficient in the method of cracking NSTimer circular references, we can just paste a picture:

Proxy message forwarding in practice

Simple little Demo

When developing, we often write code like this:

- (UIButton *)closeBtn {
    if (!_closeBtn) {
        _closeBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 150, 50)];
        [_closeBtn setTitle:@"close" forState:UIControlStateNormal];
        [_closeBtn addTarget:self action:@selector(onClickCloseBtn) forControlEvents:UIControlEventTouchUpInside];
    }
    return _closeBtn;
}

- (void)onClickCloseBtn {
    if (self.delegate && [self.delegate conformsToProtocol:@protocol(NSTimerViewControllerDelegate)]) {
        [self.delegate onClickTimerAction];
    }
}

The message forwarding process is: click button -> button selector -> delegate method,

And actually we just need: click button -> delegate method

We want to omit the step of button selector, how to do it?

Since it is a matter of message forwarding, the idea of ​​Proxy is adopted:

#import "DelegateMethodProxy.h"
#import <UIKit/UIKit.h>
#import <objc/runtime.h>

@interface DelegateMethodProxy ()

@property (nonatomic, copy) dispatch_block_t block;

@end

@implementation DelegateMethodProxy

+ (instancetype)initWithBlock:(dispatch_block_t)block {
    DelegateMethodProxy *proxy = [[DelegateMethodProxy alloc] init];
    proxy.block = block;
    return proxy;
}

- (void)addClickActionToButton:(UIButton *)view {
    objc_setAssociatedObject(view, @"DelegateMethodProxy", self, OBJC_ASSOCIATION_RETAIN);
    [view addTarget:self action:@selector(onClick) forControlEvents:UIControlEventTouchUpInside];
}

- (void)onClick {
    if (self.block) {
        self.block();
    }
}

@end

Optimize the calling method of UIButton:

- (UIButton *)closeBtn {
    if (!_closeBtn) {
        _closeBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 150, 50)];
        [_closeBtn setTitle:@"close" forState:UIControlStateNormal];
        [DelegateMethodProxy initWithBlock:^{
            if (self.delegate && [self.delegate conformsToProtocol:@protocol(NSTimerViewControllerDelegate)]) {
                [self.delegate onClickTimerAction];
            }
        }] addClickActionToButton:_closeBtn];
        [_closeBtn addTarget:self action:@selector(onClickCloseBtn) forControlEvents:UIControlEventTouchUpInside];
    }
    return _closeBtn;
}

In this way, the business layer directly implements the call link of click button -> delegate method.


**This official account will continue to update the technical solutions, pay attention to the technical trends in the industry, pay attention to the low cost, and the loss of missing dry goods is not small.
↓↓↓**
Proxy message forwarding in practice