Block/KVO/通知/代理简单介绍与使用
关于iOS开发中数据传递的方法有很多种,但是使用最多的就是这里的四种,而且我们要学会在适当的时候使用合适的方式,才能充分的提高app的性能
下面简单介绍一下这些方法的使用
Block
第一、综述
block是OC中另外一种对象和对象的通信方式,是一对一的关系,类似于delegate,而通知时一对多的关系
第二、定义block类型
int (^myBlock)(int)
第三、block的声明
mylock=^(int a)
{
int result =a*a;
return result;
}
第四、block的调用
block(20);
code sample如下所示:
int(^myBlock)(int); myBlock=^(int a){ NSLog(@"%d",a); return 0; }; myBlock(20);
第五、另外一种声明方法
typedef int(^MyBlock)(int);
MyBlock myblock=^(int a)
{
NSLog(@"%d",a);
return 30;
};
myblock(30);
第六、block作为参数传递
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//define block defination
NSLog(@"before");
MyBlock myblock=^(int a)
{
NSLog(@"%d",a);
return 30;
};
[self testBlock:myblock];
}
-(void)testBlock:(MyBlock)myblock
{
myblock(10);
}
第七、block和变量
局部变量:
在block中,会把局部变量当成常量变量
int num=10;
int num=30;
MyBlock myblock=^(int a)
{
num=20; //此处会报错
NSLog(@"%d",a);
return 30;
};
1 什么是Block:从C的声明符到Objective-C的Blocks语法 2 块代码以闭包得形式将各种内容进行传递,可以是代码,可以是数组无所不能。块代码十分方便将不同地方的代码集中统一,使其易读性增强。 3 4 来看这里怎么进行数组传递。 5 6 typedef void (^Arr_Block)(NSArray *array); 7 8 - (void)showArrayUsingBlock:(Arr_Block)block 9 { 10 NSDictionary *dict = @{@"array": @[@"Chelsea", @"MUFC", @"Real Madrid"]}; 11 NSArray *array = dict[@"array"]; 12 block(array); 13 } 14 15 调用方法,显示 16 17 - (IBAction)blockCallBack 18 { 19 [self showArrayUsingBlock:^(NSArray *array) { 20 _outputLabel.text = array[1]; 21 }]; 22 } 23 24 三种模式都很轻松~
KVO
Kvo是Cocoa的一个重要机制,他提供了观察某一属性变化的方法,极大的简化了代码。这种观察-被观察模型适用于这样的情况,比方说根据A(数 据类)的某个属性值变化,B(view类)中的某个属性做出相应变化。对于推崇MVC的cocoa而言,kvo应用的地方非常广泛。(这样的机制听起来类 似Notification,但是notification是需要一个发送notification的对象,一般是 notificationCenter,来通知观察者。而kvo是直接通知到观察对象。)
适用kvo时,通常遵循如下流程:
1 注册:
-(void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void*)context
keyPath就是要观察的属性值,options给你观察键值变化的选择,而context方便传输你需要的数据(注意这是一个void型)。
options是监听的选项,也就是说明监听泛黄的额字典包含什么值。有两个常用的选项:
NSKeyValueObservingOptionsNew
指返回的字典包含新值。
NSKeyValueObservingOptionsOld
值返回的字典包含旧值。
2 实现变化方法:
-(void)
observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void*)context
change里存储了一些变化的数据,比如变化前的数据,变化后的数据;如果注册时context不为空,这里context就能接收到。
1 1 .person类 2 @implementation Person 3 @synthesize name,age;//属性name 将被监视 4 -(void) changeName 5 { 6 name=@"changeName directly"; 7 } 8 @end 9 10 11 2.PersonMonitor类 监视了name属性 12 @implementation PersonMonitor 13 14 - (void)observeValueForKeyPath:(NSString *)keyPath 15 ofObject:(id)object 16 change:(NSDictionary *)change 17 context:(void *)context 18 { 19 if ([keyPath isEqual:@"name"]) 20 { 21 NSLog(@"change happen, old:%@ new:%@",[change objectForKey:NSKeyValueChangeOldKey],[change objectForKey:NSKeyValueChangeNewKey]); 22 } 23 } 24 @end 25 26 27 3测试代码 28 29 //初始化被监视对象 30 Person *p =[[Person alloc] init]; 31 //监视对象 32 PersonMonitor *pm= [[PersonMonitor alloc]init]; 33 [p addObserver:pm forKeyPath:@"name" options:(NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld) context:nil]; 34 35 //测试前的数据 36 NSLog(@"p.name is %@",p.name); 37 38 //通过setvalue 的方法,PersonMonitor的监视将被调用 39 [p setValue:@"name kvc" forKey:@"name"]; 40 41 //查看设置后的值 42 NSLog(@"p name get by kvc is %@",[p valueForKey:@"name"]); 43 44 //效果和通过setValue 是一致的 45 p.name=@"name change by .name="; 46 47 //通过person自己的函数来更改name 48 [p changeName]; 49 50 结果是 51 输出 52 2011-07-03 16:35:57.406 Cocoa[13970:903] p.name is name 53 2011-07-03 16:35:57.418 Cocoa[13970:903] change happen, old:name new:name kvc 54 2011-07-03 16:35:57.420 Cocoa[13970:903] p name get by kvc is name kvc 55 2011-07-03 16:35:57.421 Cocoa[13970:903] change happen, old:name kvc new:name change by .name=
通知:
通知需要有一个通知中心:NSNotificationCenter,自定义通知的话需要给一个名字,然后监听。
优点:通知的发送者和接受者都不需要知道对方。可以指定接收通知的具体方法。通知名可以是任何字符串。
缺点:较键值观察(KVO)需要多点代码,在删掉前必须移除监听者。
1 通知中心的方式可以不用设置代理,但是需要设置观察者和移除观察者。 2 3 4 5 代码 6 7 - (IBAction)callBack 8 { 9 NSDictionary *dict = @{@"array": @[@"Chelsea", @"MUFC", @"Real Madrid"]}; 10 NSArray *array = dict[@"array"]; 11 [[NSNotificationCenter defaultCenter] postNotificationName:@"OutputArrayNotification" object:array]; 12 } 13 14 注册和移出观察者 15 16 - (void)viewWillAppear:(BOOL)animated 17 { 18 [super viewWillAppear:animated]; 19 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(outputWithNote:) name:@"OutputArrayNotification" object:nil]; 20 } 21 22 - (void)viewDidDisappear:(BOOL)animated 23 { 24 [super viewDidDisappear:animated]; 25 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"OutputArrayNotification" object:nil]; 26 } 27 28 显示 29 30 - (void)outputWithNote:(NSNotification *)aNotification 31 { 32 NSArray *receiveArray = [aNotification object]; 33 _outputLabel.text = receiveArray[0]; 34 }
代理:
通过setDelegate来设置代理对象,最典型的例子是常用的TableView.
优点:支持它的类有详尽和具体信息。
缺点:该类必须支持委托。某一时间只能有一个委托连接到某一对象。
1 作为IOS中最常见的通讯模式,代理几乎无处不在。 2 3 4 5 看实例 6 这里有一个数组,我们首先通过代理的方式将数组传递到其他方法中去。 7 设置协议及方法 8 9 @protocol CallBackDelegate; 10 11 @interface ViewController : UIViewController 12 @property (weak, nonatomic) id<CallBackDelegate> delegate; 13 @end 14 15 @protocol CallBackDelegate <NSObject> 16 - (void)showArrayWithDelegate:(NSArray *)array; 17 @end 18 19 @interface ViewController () <CallBackDelegate> 20 21 点击按钮传递数组让其显示 22 23 - (IBAction)delegateCallBack 24 { 25 NSDictionary *dict = @{@"array": @[@"Chelsea", @"MUFC", @"Real Madrid"]}; 26 NSArray *array = dict[@"array"]; 27 [self.delegate showArrayWithDelegate:array]; 28 } 29 30 调用,显示 31 32 - (void)showArrayWithDelegate:(NSArray *)array 33 { 34 _outputLabel.text = array[2]; 35 } 36 37 最重要也是最容易忽略的,就是一定要设置delegate的指向。 38 完成后屏幕显示
总结:
从上面的分析中可以看出3中设计模式都有各自的优点和缺点。其实任何一种事物都是这样,问题是如何在正确的时间正确的环境下选择正确的事物。下面就讲讲如何发挥他们各自的优势,在哪种情况下使用哪种模式。注意使用任何一种模式都没有对和错,只有更适合或者不适合。 每一种模式都给对象提供一种方法来通知一个事件给其他对象,而且前者不需要知道侦听者。在这三种模式中,我认为KVO有最清晰的使用案例,而且针对某个需 求有清晰的实用性。而另外两种模式有比较相似的用处,并且经常用来给controller间进行通信。那么我们在什么情况使用其中之一呢?
根据我开发iOS应用的经历,我发现有些过分的使用通知模式。我个人不是很喜欢使用通知中心。我发现用通知中心很难把握应用的执行流程。 UserInfo dictionaries的keys到处传递导致失去了同步,而且在公共空间需要定义太多的常量。对于一个工作于现有的项目的开发者来说,如果过分的使用 通知中心,那么很难理解应用的流程。
我觉得使用命名规则好的协议和协议方法定义对于清晰的理解controllers间的通信是很容易的。努力的定义这些协议方法将增强代码的可读性,以及更好的跟踪你的app。代理协议发生改变以及实现都可通过编译器检查出来,如果没有将会在开发的过程中至少会出现crash,而不仅仅是让一些事情异常工作。甚至在同一事件通知多控制器的场景中,只要你的应用在controller层次有着良好的结构,消息将在该层次上传递。该层次能够向后传递直至让所有需要知道事件的controllers都知道。
当然会有delegation模式不适合的例外情况出现,而且notification可能更加有效。例如:应用中所有的controller需要知道一个事件。然而这些类型的场景很少出现。另外一个例子是当你建立了一个架构而且需要通知该事件给正在运行中应用。
根据经验,如果是属性层的时间,不管是在不需要编程的对象还是在紧紧绑定一个view对象的model对象,我只使用观察。对于其他的事件,我都会使用delegate模式。如果因为某种原因我不能使用delegate,首先我将估计我的app架构是否出现了严重的错误。如果没有错误,然后才使用notification。