前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于OC代理的问题:self.delegate = self

关于OC代理的问题:self.delegate = self

作者头像
用户5521279
发布2021-02-09 15:33:15
1.4K0
发布2021-02-09 15:33:15
举报
文章被收录于专栏:搜狗测试搜狗测试

背景

最近作者在进行工程代码分析时,经常看到这样的代码:

代码语言:javascript
复制
self.delegate = self    //自己的代理设置为自己

于是心中产生了不少疑问,为什么会这样写?这样写是否是正确的?带着这些疑问,我去查找了一些资料并进行了整理,希望可以分享给大家。

原因

首先我们需要了解delegate到底是什么。

Delegate 模式其实就是 NSProxy 设计模式的一种衍生版,它们共同的特点可以理解为都是传递对象的消息,主要区别如下:

1. 两者消息传递方式不同,我们使用 NSProxy 会实现消息转发功能,而 Delegate 一般不会实现,仅作消息传递。

2. Delegate 是一对一的消息传递(A->B),而 NSProxy 可以一对多的进行消息传递(A->B/A->C/A->D)。

Delegate 无非就是把 A 的消息传递给代理对象 B,self.delegate = self 直接把代理对象设置为自己,这样省去了引入第三方代理,这种做法大部分情况是为了图个方便,一般出现在使用第三方闭源代码以及系统类(如:UITextField等)的情况下,因为我们无法获知内部消息是如何传递的,只能通过代理对象获知消息。

self.delegate = self 这种做法笔者并不推荐,因为它可能会带来一些安全隐患(特别是在依赖第三方库非常多的项目中)

问题

在项目中我们经常会用到 UITextField 类或者其子类,有时候为了图其方便会把 UITextField 的 delegate 设置为自己(self.delegate = self),然而在使用 UITextField 控件时,发现程序不响应了,过了几秒后程序出现闪退现象。

既然 Bug 来了,那当然就是找 Bug,于是我们开始排查原因(先撇开调用栈信息):

1. 首先针对新增的部分代码进行注释,把 self.delegate = self 代码注释掉,然后重新运行程序,发现问题得到解决。

2. 控制变量法开始排查。难道是 self.delegate = self 导致的?

于是新建工程,写了一份一模一样的代码(注:SGLimitedTextField 继承自 UITextField):

代码语言:javascript
复制
@implementation ViewController   - (void)viewDidLoad {  [super viewDidLoad];  SGLimitedTextField *textField = [[SGLimitedTextField alloc] initWithFrame:CGRectMake(100, 100, 100, 30)];  textField.backgroundColor = [UIColor redColor];  textField.delegate = textField;  [self.view addSubview:textField];}    @end

运行新建的工程后,发现没有这问题。于是在 SGLimitedTextField.m 文件中再实现自己的代理方法:

代码语言:javascript
复制
@interface SGLimitedTextField ()
@end    @implementation SGLimitedTextField    - (BOOL)textFieldShouldReturn:(UITextField *)textField {   [textField endEditing:YES];   return YES;}    @end

运行工程,使用 SGLimitedTextField 控件,发现还是没有这问题。而进行全局断点后,重新再次运行项目,发现调用栈无限递归,直到栈溢出,最后导致程序崩溃。

原因

既然查到了无限递归,那我们就需要查找是否存在这种无限递归的代码

代码语言:javascript
复制
- (void)doSomething {    if ([self.delegate respondsToSelector:@selector(doSomething)]) {        [self.delegate performSelector:@selector(doSomething)];    }}

于是开始分析代码,找到了程序崩溃点,找到了程序的崩溃点后,通过 NSLog 输出上述方法中的选择器 selector,发现是 -keyboardInputChangedSelection: 方法,于是设置条件断点,如图所示:

进入断点调试后,发现一个有意思的事,如图所示:

这说明,在 UITextField 中,伪代码如下:

代码语言:javascript
复制
- (id)keyboardInputChangedSelection:(id)obj {    // self == UITextField    if ([self.delegate respondsToSelector:@selector(keyboardInputChangedSelection:)]) {        [self.delegate keyboardInputChangedSelection:obj];    }}

此时,细心的读者可能会产生一个疑惑,如果如上所述,那么上文提到新建的工程(SGLimitedTextField 类,如果写了 self.delegate = self)也应该会出现无限递归(死循环)才对啊?

然而事实上却没发生死循环。

作者通过断点调试,发现同样会调用 -keyboardInputChangedSelection:,断点截图同上,但不会出现死循环,最终导致程序崩溃的现象,笔者猜测分析,UITextField 类应该针对 self.delegate = self 做了一些特殊的处理,具体什么处理,就得问苹果爸爸了。可以肯定的是,在没有任何方法调剂的情况下,即 “self.delegate == self”,是不会出现死循环的问题的。

问题解决

通过上文主要以 UITextField 为例进行讨论分析,那么这种问题应当如何解决?

1. 在没有考虑清楚前,避免使用 self.delegate = self。

2. 破除死循环,解决上述问题,只需停止消息转发即可。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 搜狗测试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档