情谊提醒:点击查看本文所波及的 demo 代码
导语
代理即是代表受权方处理事务(From Wikipedia)。
思考一下咱们生存中什么时候会用到代理呢?
租房、买房时,咱们须要一位中介帮咱们分割房东,解决手续上的事件,升高咱们和房东的沟通老本。叫外卖时,咱们须要外卖小哥帮咱们送外卖,好让咱们有更多工夫去专一别的事件。
所以能够了解为中介帮咱们解决两个层面上的问题:
- 缩小相互依赖的问题
- 缩小做反复的事件
所以从实质上来说,Proxy 体现的还是 ” 中间层 ” 的设计思维,具体利用于 ” 音讯转发 ” 的业务场景。
循环援用
在讲述明天这个 Demo 前,咱们先回忆一下之前咱们接触过的 Proxy 的利用场景,我想你脑海中必定第一工夫浮现出:应用 Proxy 解决 NSTimer 循环援用的问题。
所以咱们首先聊一聊 Proxy 应用最刚需的「解决循环援用」的场景。
循环援用是怎么产生的
下图是内存失常回收的过程:
上面是产生循环援用导致内存透露的过程:
验证是否产生循环援用的最佳形式就是判断是否产生了一个援用环。
NSTimer 循环援用问题
NSTimer 问题最乏味的点是,网上对于 NSTImer 为什么会导致循环援用的解释 80% 都是不清晰的,比方这样一个最广泛的说法:
这样的说法就好似有人问小明:”NSTimer 为什么会导致循环援用?”
小明却答复:”NSTimer 会导致循环援用 ”。
演出了一出 ” 搁着搁着呢 ” 的好戏😂。
循环援用肯定是 ViewController 和 NSTimer 互相强援用,但为什么 NSTimer addTarget 会导致循环援用,但平时咱们应用的 UIButton addTarget却不会导致循环援用呢?
答复分明这个问题,才算是说分明了 ”NSTimer 为什么会导致循环援用 ”。
其实解答这个问题也很简略😂 咱们查一下大苹果提供的文档阐明,
对于 UIControl:
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.
翻译成中文含意就是:控件不保留指标参数中的对象。在指标对象附加到控件时,保护对指标对象的强援用是您的责任
对于 NSTimer 阐明如下:
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.
中文含意是:当定时器触发时,要发送由 aSelector 指定的音讯到的对象。计时器保护对该对象的强援用,直到它 (计时器) 生效。
这样一看就明确多了,之所以 NSTimer 会导致强援用,但 UIControl 不会导致强援用,是大苹果的 feature,达到了真正的类比深刻的成果。
破解 NSTimer 循环援用的办法咱们都很纯熟了,咱们贴一张图即可:
简略小 Demo
开发时咱们常常会写出这样的代码:
- (UIButton *)closeBtn {if (!_closeBtn) {_closeBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 150, 50)];
[_closeBtn setTitle:@"敞开" 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];
}
}
音讯转发流程是:click button -> button selector -> delegate method,
而实际上咱们只须要:click button -> delegate method
咱们想省去 button selector 这个步骤,怎么做呢?
既然是音讯转发的事件,那就采纳 Proxy 的思路:
#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
优化 UIButton 的调用办法:
- (UIButton *)closeBtn {if (!_closeBtn) {_closeBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 150, 50)];
[_closeBtn setTitle:@"敞开" 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;
}
如此一来,业务层就间接实现了 click button -> delegate method 的调用链路。
** 这个公众号会继续更新技术计划、关注业内技术动向,关注一下老本不高,错过干货损失不小。
↓↓↓**