OC 中的协议的一些注意点
2021-06-24 本文已影响0人
望穿秋水小作坊
1. 把相同的属性和方法抽取出来,有哪两种做法?
- ① 构建父类
- ② 构建协议
2. 上述的两种做法,有什么不同?
- ① 类的属性和方法都是和这个类关联的
- ② 协议的属性和方法不和任何类进行关联,是独立的
3. OC 中调用遵守协议中的方法时,为了确保安全,要怎么做?
- 要使用 respondsToSelector 进行协议检查,确保确实真的实现了该协议方法
4. 有空练习一下(就能比较彻底明白,UITableViewDelegate 和 UITableViewDataSource)
-
MyView.h
代码如下
// 协议可以用来 view 显示的数据源,也可以用来 view 的代理使用,典型的例子就是 UITableView;
// 有 返回值 的作为数据源使用,没有返回值的作为 代理 使用
#import <UIKit/UIKit.h>
@protocol MyViewDataSource <NSObject>
- (NSUInteger)numbersOfPeople;
- (NSString *)titleForView;
@end
@protocol MyViewDelegate <NSObject>
- (void)myViewPayButtonDidClick;
@end
@interface MyView : UIView
@property (nonatomic, weak, nullable) id<MyViewDataSource> dataSource;
@property (nonatomic, weak, nullable) id<MyViewDelegate> delegate;
- (void)reload;
@end
-
MyView.m
代码如下
#import "MyView.h"
@interface MyView ()
@property(nonatomic, strong) UILabel *titleLabel;
@property(nonatomic, strong) UILabel *peopleLabel;
@property(nonatomic, strong) UIButton *payButton;
@end
@implementation MyView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 300, 44)];
self.titleLabel.text = @"默认标题";
[self addSubview:self.titleLabel];
self.peopleLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 200, 300, 44)];
self.peopleLabel.text = @"默认人数 0";
[self addSubview:self.peopleLabel];
self.payButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.payButton.frame = CGRectMake(20, 300, 300, 44);
[self.payButton setTitle:@"付款按钮" forState:UIControlStateNormal];
[self addSubview:self.payButton];
[self.payButton addTarget:self action:@selector(payButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)reload {
if (self.dataSource && [self.dataSource respondsToSelector:@selector(titleForView)]) {
self.titleLabel.text = [self.dataSource titleForView];
}
if (self.dataSource && [self.dataSource respondsToSelector:@selector(numbersOfPeople)]) {
self.peopleLabel.text = [NSString stringWithFormat:@"具体人数 %lu", (unsigned long)[self.dataSource numbersOfPeople]];
}
}
- (void)payButtonAction {
if ([self.delegate respondsToSelector:@selector(myViewPayButtonDidClick)]) {
[self.delegate myViewPayButtonDidClick];
}
}
@end
-
ProtocolViewController.m
调用代码如下
#import "ProtocolViewController.h"
#import "MyView.h"
@interface ProtocolViewController ()<MyViewDataSource, MyViewDelegate>
@property(nonatomic, strong) MyView* myView;
@property(nonatomic, assign) NSUInteger peopleCount;
@end
@implementation ProtocolViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.myView = [[MyView alloc] init];
[self.view addSubview:self.myView];
[self.myView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
self.myView.dataSource = self;
self.myView.delegate = self;
}
- (void)myViewPayButtonDidClick {
NSLog(@"控制器收到点击事件");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"touchesBegan");
[self.myView reload];
}
- (NSUInteger)numbersOfPeople {
return ++self.peopleCount;
}
- (nonnull NSString *)titleForView {
return @"来自控制器的标题";
}
@end
5. 思考如下问题
- 如果把上述代码中
MyView.h
的 dataSource 改为如下声明
@property (nonatomic, weak, nullable) id dataSource;
- 请问在
MyView.m
文件中,下面代码能编译
通过吗(不考虑运行时错误)?
- (void)reload {
[self.delegate myViewPayButtonDidClick];
}
- 上述代码能编译通过,但是在运行时会报错
- 按照目前的理解是类型为
id类型
的实例可以调用它能拿到的所有成员方法
- 在
Class
身上有同样的特质,能调用 Class 在当前上下文中访问到的所有类方法
Class myClass = nil;
[myClass canCallAllClassMethod];