代理:
第一就是代理,这也是很常用的方式,特点是一对一
的形式,而且逻辑结构非常清晰。实现起来较为简单:
1.声明协议
2.定义代理这个属性,
3.设置代理
最好在你想通知代理做事情的方法中调用即可。当然这里面有一些细节,包括
①协议定义时,请用关键字@required,和@optional来明确代理是否必须实现某些方法
②代理的类型需用id类型,并写明要遵守的协议,为防止循环引用代理属性要用copy
③就是在调用代理方法的时候需要判断代理是否实现该方法,以避免代理在回调时因为没有实现方法而造成程序崩溃。
适用场景:
公共接口,回调方法较多也选择用delegate进行解耦
iOS最常用tableViewDelegate,textViewDelegate
通知:
通知的优点很明显,他是一对多
的形式,而且可以在任意对象之间传递,不需要二者有联系,当然他的实现和代理相比较稍微绕一点,使用通知主要就3步:
1.在通知中心注册,
2.发通知,
3.收通知。
这里面的注意点就是:
①对于系统没有定义的事件监听时需要自己发通知,这是你就需要定义一个key,是字符串类型,这也是通知的一个弊端,你需要拷贝到收通知的对象,避免写错一个字母而无法收通知的尴尬,可以定义成宏.
②就是注册的通知中心需要手动移除,不然除了性能的问题还会有其他的问题出现,比如说一个控制器消失了之后还有因为某些事件而发出通知,造成不想要的结果。
使用场景:
1: 很多控制器都需要知道一个事件,应该用通知;
2 :相隔多层的两个控制器之间跳转
block:
block是C语言的,类是于C的函数
- 它是一种数据类型,可以当参数传递
- 说的简单点block就是一组预先准备好的代码,在需要的时候执行,无论在哪调用,block的执行都会回到block创建的地方执行
使用场景:
异步和简单的回调用block更好
Block最典型的是大所周知的AFNetWorking第三方库
注意点:
1.当block作为属性时,要用copy修饰
因为防止在MRC下,如果block访问了外部变量,那么block就会存在栈区,当Block所在的作用域结束时就会被销毁,所以当在别的地方回掉时可能就会出错,为了避免这种情况就需要手动调用Block的copy方法copy到堆区,这也是Block当属性时用copy的原因.在MRC下需要手动管理内存(需要程序猿手动copy到堆区)
2.____weak
Block还有一个注意点就是,Block会对外部使用的变量和控制器进行强引用,为避免循环引用造成资源释放不了,需要用__weak修饰外部变量和控制器
3.__block
block内部默认是不能修改外部变量的值,如果要修改使用__block修饰,一旦用__block修饰了,变量的地址就变成了堆区的地址.
KVO:
翻译过来就是"键值观察",用来监听对象的属性值的变化,指定的被观察的对象的属性被修改后,KVO就会通过回调方法自动通知相应的观察者了,KVO和通知有点相似,使用步骤:
- 注册,指定被观察者的属性,
- 实现回调方法
- 移除观察
KVO底层原理实现: KVO是基于runtime机制实现的
什么是isa?
每一个对象内部都有一个 isa 指针,这个指针是指向它的真实类型
根据这个指针就能知道将来调用哪个类的方法
KVO前后的isa变化
在addObserver:forKeyPath:options:context:之后,利用objective-c的runtime机制
把象p的isa变为了NSKVONotifying_Person
。
所以,根据上面的isa介绍,响应setAge的方法,实际上是对象p的isa即NSKVONotifying_Person类的setAge方法,并不是原Person类的setAge方法。
小结:当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类
的一个派生类,在这个派生类中重写基类中任何被观察属性的
setter 方法。
区别:
1.大多数情况下看个人习惯.
2.但是, 一般在层级结构比较深又需要传值的情况,用通知比较合适.
3.代理比通知规范,非常适合多人开发以及代码维护,还有就是当通信事件比较多时,建议使用代理;
4.KVO仅仅是能监听对象属性的改变(灵活度不如通知和代理),比如,监听tableView的contentOffSet
5.block,异步和简单的回调用block更好,例如,一些网络库,AFNetworking