Effective Objective-C 2.0 学习记录(一)
Effective Objective-C 2.0 学习记录(二)
十一、协议(protocol)与分类(category)
我们可以把某个类应该实现的一系列方法定义在协议里面,最为常见的用途就是实现委托(delegate)模式
利用分类我们无须继承子类就可以为当前类添加方法。
1、delegate例子:(当然也可以用block,kvo/kvc,notification)
我们实现一个简单的功能,在当前页面A(ViewController)有个Label,显示一段文字,点击按钮进入下一个页面B(NextViewController),在页面B中有个Label,点击按钮返回页面A,此时页面A中的Label的文本内容改为B中Label的内容。
A页面中:
//.h文件
#import <UIKit/UIKit.h>
#import "NextViewController.h"
@interface ViewController : UIViewController<YCFShowContentDelegate>
@property (nonatomic, strong) UILabel *showLabel;
@end
//.m文件
- (void)viewDidLoad {
[super viewDidLoad];
//进入下个页面按钮
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, 100, 30)];
[btn addTarget:self action:@selector(onClickBtn) forControlEvents:UIControlEventTouchUpInside];
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
_showLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, 30)];
_showLabel.text = @"A 中 Hello world";
[self.view addSubview:self.showLabel];
}
- (void)onClickBtn {
NextViewController *ycf = [[NextViewController alloc] init];
ycf.delegate = self;//一定要写,否则回调不会执行
[self presentViewController:ycf animated:YES completion:nil];
}
//delegate回调
- (void)showContent:(NSString *)content{
_showLabel.text = content;
}
B页面中:
//.h文件
#import <UIKit/UIKit.h>
@protocol YCFShowContentDelegate <NSObject>
//对于委托实现的方法我们可以用optional来修饰,告知被委托的页面,并不是所有的方法都要实现,都则会有警告告知用户又些委托方法没有实现。对于有强迫症的大大来说,这样解决是最好了。
@optional
- (void)showContent:(NSString *)content;
@optional
- (void)reLoadCurrentVC;
@end
@interface NextViewController : UIViewController
@property (nonatomic, weak) id <YCFShowContentDelegate> delegate;
@end
//.m文件
#import "NextViewController.h"
@interface NextViewController (){
UILabel *showLabel;
}
@end
@implementation NextViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
showLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, 30)];
showLabel.text = @"this is next viewcontroller!";
[self.view addSubview:showLabel];
UIButton *changeBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, 60, 40)];
[changeBtn setTitle:@"返回显示label内容" forState:UIControlStateNormal];
changeBtn.backgroundColor = [UIColor lightGrayColor];
[changeBtn addTarget:self action:@selector(onClickBtn) forControlEvents:UIControlEventTouchUpInside];
[changeBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.view addSubview:changeBtn];
}
- (void)onClickBtn{
//在这里一定要判断下,是否被委托的页面实现了该方法,不判断的话程序会crash。
if (_delegate && [_delegate respondsToSelector:@selector(showContent:)]) {
[_delegate showContent:showLabel.text];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
2、category:通过分类机制,可以把类代码分成很多个易于管理的小块,便于调试,不过在需要使用的地方要引入分类的头文件。
记得面试时,问过一个问题:在项目中,有很多网络请求的数据,但是有可能服务端对数据没有进行设置,又些数据为空,到客户端要是没有处理,直接取值,程序可能会crash,用一种方法来防止在这种情况下程序的crash。
虽然在每个关于网络取值的地方我们每取一个值就进行判断,但是页面多,每次都要判断,不方便。我们可以为NSArray写个categroy。
1)、新建一个catorgy
2)、写一个判断取值的方法,把会crash的情况考虑到
//.h文件
@interface NSArray (YCFKeepIndexSafe)
- (id)ycf_objectIndexCheck:(NSUInteger)index;
@end
//.m文件
- (id)ycf_objectIndexCheck:(NSUInteger)index{
if (index >= self.count) {
return nil;//如果不想在页面上显示“null”,则可以返回@"";
}
id value = [self objectAtIndex:index];
if (value == [NSNull null]) {
return nil;//如果不想在页面上显示“null”,则可以返回@"";
}
return value;
}
我们在一个VC中测试下:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *arrList = [[NSArray alloc] initWithObjects:@"Sam",@"Yuna",,@"Vernal", nil];
NSLog(@"%@",[arrList ycf_objectIndexCheck:4]);//此时不会crash
}
注意:我们在给分类中添加方法的时候,方法名加上前缀是为了防止方法名重复。引起不必要的bug。