-
实现含有位段的结构体,将委托对象是否能响应相关协议方法的信息缓存其中
在委托协议中,方法一般都是可选的(optional)。如:
@protocol myTestViewDelegate <NSObject>
@optional
- (void)clickScreen:(myTestView *)mytestView;
@end
如果我们要在委托对象中调用可选择的方法,必须得提前使用类型查询信息方法判断这个委托对象能否响应相关的选择子(响应时给nil发送信息,将会使得if语句里的值为false)。如:
if ([_delegate respondsToSelector:@selector(clickScreen:)])
{
[_delegate clickScreen:self];
}
这样子,delegate对象就可以完全按照需要来实现委托协议的方法,不用担心其中某个方法没有实现而导致程序崩溃。又因为这样能查出某个委托对象是否能够响应选择子,除了第一次检测结果有用之外,后续的再次检测有可能都是多余的。
所以,可以将能否响应协议方法的信息缓存起来,优化程序效率。假设在某个需求中,需要多次执行方法,每次都会执行delegate回调方法,这时候每次都去检查委托对象是否响应该选择子,就显得多余了。比如:系统的touchesMoved事件,都需要执行一次委托协议的方法:
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if ([_delegate respondsToSelector:@selector(clickScreen:)])
{
[self.delegate clickScreen:self];
}
}
这时候将方法响应缓存起来的最佳途径是使用”位段“数据类型(C语言的特性)。我们可以把结构体中某个字段所占用的二进制位个数设为特定的值。以上面的方法为例,此结构体可以写成如下:
struct {
unsigned int didRespondToClickScreen : 1;
// 这里可扩展更多的选择子,如:
// unsigned int didRespondToLongPressScreen : 1;
} _myDelegateFlags;
然后在本类中,设置结构体中的位段:
- (void)setDelegate:(id<myTestViewDelegate>)delegate
{
_delegate = delegate;
_myDelegateFlags.didRespondToClickScreen = [self.delegate respondsToSelector:@selector(clickScreen:)];
}
这样的话,在每次调用delegate中的方法之前,就不用检测委托对象是否能够响应特定的选择子,而是直接查询结构体中的标志,上面touchesMoved中的例子可以改写成:
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (_myDelegateFlags.didRespondToClickScreen)
{
[self.delegate clickScreen:self];
}
}
在相关方法调用很多次的时候,就值得这样去优化。如果频繁地通过数据源协议、委托协议中获取独立的数据或者回调的时候,这种优化极有可能提高程序效率。
完整代码:
myTestView.h
//
// myTestView.h
// MyTest
//
// Created by Yim on 2021/5/17.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class myTestView;
@protocol myTestViewDelegate <NSObject>
@optional
- (void)clickScreen:(myTestView *)mytestView;
@end
@interface myTestView : UIView
@property (nonatomic, weak) id <myTestViewDelegate> delegate;
@end
NS_ASSUME_NONNULL_END
myTestView.m
//
// myTestView.m
// MyTest
//
// Created by Yim on 2021/5/17.
//
#import "myTestView.h"
struct {
unsigned int didRespondToClickScreen : 1;
// 这里可扩展更多的选择子,如:
// unsigned int didRespondToLongPressScreen : 1;
} _myDelegateFlags;
@implementation myTestView
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (_myDelegateFlags.didRespondToClickScreen)
{
[self.delegate clickScreen:self];
}
}
- (void)setDelegate:(id<myTestViewDelegate>)delegate
{
_delegate = delegate;
_myDelegateFlags.didRespondToClickScreen = [self.delegate respondsToSelector:@selector(clickScreen:)];
}
@end
ViewController.m
//
// ViewController.m
// MyTest
//
// Created by Yim on 2021/5/17.
//
#import "ViewController.h"
#import "myTestView.h"
@interface ViewController () <myTestViewDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
myTestView * testView = [[myTestView alloc]initWithFrame:CGRectMake(50, 50, 100, 100)];
testView.delegate = self;
testView.backgroundColor = [UIColor cyanColor];
[self.view addSubview:testView];
}
- (void)clickScreen:(myTestView *)mytestView
{
NSLog(@"点击了屏幕");
}
@end
网友评论