iOS
中关于信息传递过程中经常会使用block
,委托代理,NSNotification
,KVO
进行调用,但是查找资料后有些资料不是很全。
四者总体比较然后进行一一详解进行讲解:
block
:
一对一进行通信,比起其他三者更加简洁,但是事件比较多时可以使用delegate
。
- 委托代理:
和block
一样是一对一,我们使用时要先进行协议方法然后实现协议代理,如果需要通信就需要实现代理。
NSNotification
:
在进行注册监听时,可以进行一对多的情况,一个进行注册可以多种情况下进行监听。
KVO
:就是我们所说的键值监听模式,其主要是在KVC基础上完后才能。
以前读过一个砍柴的故事,如果我们想要追求搞得效率就要对所用到的工具了解,下面我们就开始磨我们搜中的刀。
block
来龙去脉:
block
是iOS SDK 4.0
中引入的,block
在iOS
中实际就是一个代码块,有点像C++
中内联函数inline
有点相似,最让人惊奇的是我们还可以向其传递参数。闲来没事想要知道block
在C++
中的具体实现就是用Clang
编辑^{printf"Hello, World!"}()
,此时我们在block
仅仅是输出一个语句。//hello.c
是我们把想要编辑的内容放置的文件名称
block
在没有传入参数情况
1 | $ clang -rewrite-objc hello.c |
在编译后再文件后会生成 hello.cpp
文件
1 | struct __mian_block_impl_0 |
上面的代码既是我们通过编译截取的hello.cpp
的具体代码,根据上面代码我们可以看到block
实际是struct
结构。看出_mian_block_impl_0
其中传入其中也是block_impl&mian_block_desc_0
两个block
,下面是_mian_block_impl_0
的构造函数。
block
在有传入参数情况
1 | clang -rewrite-objc hello1.c |
同样在我们编译过后会生成hello.cpp
1 | struct __mian_block_impl_0 { |
可以看出在block
中使用外部变量perperson
时,block
初始化时这样的:
是在__mian_block_impl_0
进行初始化perperson
,在block
构造时作为block
的其中
一个成员初始。在访问时通过__cself->perperson
指针进行访问。
block
在有传入参数情况
1 | clang -rewrite-objc hello2.c |
1 | struct __Block_byref_perperson_0 { |
我们在对于block
外的perperson
计算重新复制,和上面在block
仅仅在block
中使用外面的参数。
两者经过对比可以看出:
在block
中对于外部值进行复制,会出现下面的情况。使用clang
对于block
进行编码,可以看书对于perperson
进行_block
的修饰后编码生成Block_byref_perperson_0
的struct
结构体,其中__Block_byref_perperson_0
的指针指向在block
中perperson
的地址。
1 | struct __Block_byref_perperson_0 { |
block
的使用方法~传入参数:
1 | int (^isInputEven)(int) = ^(int n){ |
上面我们定义一个block
代码段,代码的实现功能是利用递归计算n
的基乘。代码可以作为整个代码块进行调用,而且在调用过程中我们只需isInputEven()
函数名和传入参数即可实现。
block
的使用方法~使用外部参数参数:
1 | float perperson = 34.5; |
使用外部参数perperson
,让我们感觉block
可以想一个类一样使用socpe
里面的变量目前无法做到。
主要原因是因为:
在block
的编译过程中我们看到,在block
中进行访问其中元素时需要是block
默认构造函数对于block
其中的元素访问。
block
的使用方法~使用外部参数参数:
1 | __block float perperson = 34.5; |
block
的使用方法~在(UIView)
的动画中使用:
1 | [UIView animateWithDuration:3.0f animations:^{ |
iOS
的开发到今天差不多已经有7
年之久,block
在开发中使用广度越来越广泛:
- 枚举–来过去对象。例如:
NSDictionary
的枚举过程 UIView
–对于UIView
的动画设置,后面有机会会对动画专门讲述…- 通知–在本文讲述的就是在调用过程中与其他通知不同点所在
- 完成处理–在相应程序结束后,需要对于程序结果的处理。例如:
AFNetWork
在访问网络后,对于成功和失败进行处理 GCD
–在GCD
的过程中都是含有block
使用的方法- 排序–平时我们使用的一些简单算法均可以
block
进行相关处理
委托代理详解
在发C++
过程中经常见到多组继承,但是在iOS
开发过程中只能进行单继承,很多方法我们需要实现接口的形式。正如我们在使用UITableView
时,经常会用到UITableViewDataSource
和UITableViewDelegate
。
如果要使用使用委托代理,需要知道协议和委托两者关系。
协议
协议一般分为两种:
@ required: //是我们在继承过程中必须实现的
@optional : //在实现接口中需要进行选择性实现的方式
委托
常见的一种设计模式,身为老板一般负责管理员工、打电话、发薪水,这样由于公司业务发展就请一个秘书负责:打电话和发薪水。
- 首先进行协议提取(需要员工所得事情):(
boss.h
文件中声明)
1 | @protocol protocol <NSObject> |
- 对于老板在具体工作的实现:(
boss.m
文件中实现)
1 | @implementation boss |
- 委托秘书所要做的事物:(在
sec.m
的文件中实现完成boss
的代理)
1 | @interface Sec ()<protocol> |
注册监听小试
NSNotificationCenter
就相当于广播一样,可以对对象进行一次注册然后有多个监听。就像我们知道如果有注册的话,就需要我们对于对象进行释放(remove)
。
1 | [[NSNotificationCenter defaultCenter] postNotificationName:@"clickbt" object:nil]; |
在上面的代码可以看出我们在使用注册监听时可以进行相关内容传递,需要传递的内容放在object
(为id
类型)。
在remove
监听的方法,经过查找资料:
1 | -(void)viewWillAppear:(BOOL)animated |
KVO
简史
当我们说起KVO(key-value-observe)
时不免想起他的孪生大哥KVC(key-value-coding)
。KVO
是观察者模式的继承者,基于键值变化监听者,基于KVC
基础完成之一。
- 下列对于界面的UILabel进行监听:
1 | [self.label addObserver:self forKeyPath:@"narcotics" options: |
- 一旦
UILabel
发生变化就会调用方法:
1 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object |
附带:
KVC
:通过设置key
值,进行标记。然后通过key
值找到进行重新设置,说的通俗点点就是键值对。类似NSDictionary
相似。
以前经常看些大牛写的博客,在看博客过程中也学到很多知识。心向往之,开始动手写关于自己在iOS
成长路上见证。
简述block
和delegation
使用场景的比较
我们知道block
和delegation
是关于通信方面,在开发的过程中我们也经常会使用两者。面对具体情况我们需要怎么样做出选着呢?
1.当在一个方法的参数中需要多个对象时使用delegation
我们在开发过程中经常使用UITableView
,这时我们就要实现接口协议UITableViewDataSource
, UITableViewDelegate
两者是我们在使用UITableView
的相关数据协议和相关协议。从tableView角度出发:实现展示需要数据、对一些展示过程的控制等,这些可以使用block
来进行实现。
1 | void (^ showTableView)(NSData *data, NSInteger number, CGFloat heightForRowAtIndexPath); |
上面我们定义告诉过程需要参数在NSIndexPath
,所以第三个参数应该是 block
形式。我们上面知道block在编译过程会被编译为struct
结构,在相互嵌套过程中使用delegation
更加方便。
2. 一个对象只能有一个delegation
由于一个对象只能有一个delegate
,而且它只能与这个delegate
通信。让我们看看CLLocationManager
这个类,当发现地理位置后,location manager
只会通知一个对象(有且只有一个)。当然,如果我们需要更多的对象去知道这个更新,我们最好创建其他的location manager
。
这里有的人可能想到,如果CLLocationManager
是个单例呢?如果我们不能创建CLLocationManager
的其他实例,就必须不断地切换delegate
指针到需要地理数据的对象上(或者创建一个只有你理解的精密的广播系统)。因此,这样看起来,delegatetion
在单例上没有多大意义。
关于这点,最好的印证例子就是UIAccelerometer
。在早期版本的iOS
中,单例的 accelerometer
实例有一个delegate
,导致我们必须偶尔切换一下。这个愚蠢的问题在之后的iOS
版本被修改了,现在,任意一个对象都可以访问CMMotionManager block
,而不需要阻止其他的对象来接收更新。
因此,我们可以得出另一个结论:“如果一个对象是单例,不要使用delegation
”。
3. 一般delegation
都有自己的而返回值
还是拿上面的例子说明:
1 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; |
上面是UITableViewDataSource
协议中必须实现协议,可看出是使用返回值。而我们也可以看其他的一些协议表示情况,一般均是需要返回值的。
4.delegation
偏向于使用的过程VS block
偏向于使用结果
如果查看NSURLConnectionDelegate
以及 NSURLConnectionDataDelegate
,我们在可以protocol
中看到这样的消息:我将要做什么(如: willSendRequest
,将要发送请求)、到目前为止我知道的信息(如:canAuthenticateAgainstProtectionSpace
)、我已经完成这些啦( didReceiveResponse
,收到请求的回复,即完成请求)。这些消息组成一个流程,而那些对流程感兴趣的delegate
将会在每一步得到相应的通知。
当我们观察handler
和完整的方法时,我们发现一个block
包含一个响应对象和一个错误对象。显然这里没有任何有关“我在哪里,我正在做什么的”的交互。
因此我们可以这样认为,delegate
的回调更多的面向过程,而block
则是面向结果的。如果你需要得到一条多步进程的通知,你应该使用delegation
。而当你只是希望得到你请求的信息(或者获取信息时的错误提示),你应该使用block
。(如果你结合之前的3个结论,你会发现delegate
可以在所有事件中维持state
,而多个独立的block
确不能)。