iOS响应者链, runtime, runloop

释放双眼,带上耳机,听听看~!

响应者链

响应者链条概念:iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动Application的事件队列,单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,UIWindow对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,这个过程称之为hit-test view。

当一个点击touch事件发生时, 设备首先会先找到touch发生的视图:
执行顺序:
1. 设备将手指触摸UITouch 和 事件UIEvent对象打包, 放到当前活动的Application的事件列表中
2. 单例的UIApplication会从事件队列中取出触摸事件并传递给单例UIWindow
3. UIWindow使用hitTest:withEvent:方法查找touch操作的所在的视图view

查找方法: (从UIWindow开始->viewController->...->hit-test view)
UIWindow实例对象会首先在它的内容视图上调用`hitTest:withEvent:`,
此方法会在其视图层级结构中的每个视图上调用`pointInside:withEvent:`
(该方法用来判断点击事件发生的位置是否处于当前视图范围内,以确定用户是不是点击了当前视图),
如果pointInside:withEvent:返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息,直到找到touch操作发生的位置,这个视图也就是要找的hit-test view。

上面找到了事件的第一响应者,接下来就该沿着寻找第一响应者的相反顺序来处理这个事件,如果UIWindow单例和UIApplication都无法处理这一事件,则该事件会被丢弃。

说明:
1、如果最终 hit-test没有找到第一响应者,或者第一响应者没有处理该事件,则该事件会沿着响应者链向上回溯,如果UIWindow实例和UIApplication实例都不能处理该事件,则该事件会被丢弃;

2、hitTest:withEvent:方法将会忽略

2.1 隐藏(hidden=YES)的视图

2.2 禁止用户操作(userInteractionEnabled=NO)的视图

2.3 alpha级别小于0.01(alpha < 0.01)的视图

2.4 如果一个子视图的区域超过父视图的bound区域(父视图的clipsToBounds属性为NO,这样超过父视图bound区域的子视图内容也会显示),那么正常情况下对子视图在父视图之外区域的触摸操作不会被识别,因为父视图的pointInside:withEvent:方法会返回NO,这样就不会继续向下遍历子视图了。当然,也可以重写pointInside:withEvent:方法来处理这种情况。

触摸事件的响应者:

- (IBAction)logResponse:(id)sender {
for (UIResponder *next = sender; next; next = [next nextResponder]) {
NSLog(@"--%@--", next);
}
}

输出结果:

1. --<UIButton: 0x7fa13842e4d0; frame = (148 483; 77 33)>--
2. --<UIView: 0x7fa13852b360; frame = (0 0; 414 736)>--
3. --<ViewController: 0x7fa138426720>--
4. --<UIWindow: 0x7fa138428290; frame = (0 0; 414 736)>--
5. --<UIApplication: 0x7fa13840b4b0>--
6. --<AppDelegate: 0x7fa138711b40>--

runtime

RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪个函数。编译完成之后直接顺序执行,无任何二义性。OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。而C语言在编译阶段就会报错) 只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

  1. runtime实现的机制是什么,怎么用,一般用于干嘛?

    runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。
    在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者
    比如说,下面一个创建对象的方法中,
    举例:

[[ZHZPerson alloc] init]
runtime ->
objc_msgSend(objc_msgSend(“ZHZPerson” , “alloc”), “init”)
  1. runtime 用来干什么呢??用在那些地方呢?怎么用呢?

    runtime是属于OC的底层, 可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)

    2.1 在程序运行过程中, 动态创建一个类(比如KVO的底层实现)

    2.2 在程序运行过程中, 动态地为某个类添加属性方法, 修改属性值方法

    2.3 遍历一个类的所有成员变量(属性)所有方法

    例如:我们需要对一个类的属性进行归档解档的时候属性特别的多,这时候,我们就会写很多对应的代码,但是如果使用了runtime就可以动态设置!

    可以在运行时,在不继承也不category的情况下,为各种类(包括系统的类)做很多操作,具体包括:

增加

1. 增加函数:class_addMethod
2. 增加实例变量:class_addIvar
3. 增加属性:@dynamic标签,或者class_addMethod,因为属性其实就是由getter和setter函数组成
4. 增加Protocol:class_addProtocol 

获取

1. 获取函数列表及每个函数的信息(函数指针、函数名等等):class_getClassMethod method_getName ...
2. 获取属性列表及每个属性的信息:class_copyPropertyList property_getName
3. 获取类本身的信息,如类名等:class_getName class_getInstanceSize
4. 获取变量列表及变量信息:class_copyIvarList
5. 获取变量的值

替换

1. 将实例替换成另一个类:object_setClass
2. 将函数替换成一个函数实现:class_replaceMethod
3. 直接通过char *格式的名称来修改变量的值,而不是通过变量

runloop

基本作用

  1. 保持程序的持续运行
  2. 处理app中的各种事件(触摸, 定时器事件, Selector事件)
  3. 节省CPU资源, 提高程序性能: 该做事的时候做事, 该休息时休息

什么是RunLoop

  1. 从字面意思看: 运行循环;
  2. 其实它内部就是do-while循环, 在这个循环内部不断处理各种任务(比如source, Timer, Observer)
  3. 一个线程对应一个Runloop, 主线程的Runloop默认已经启动, 子线程的Runloop得手动启动(调用run方法)
  4. Runloop只能选择一个Mode启动, 如果当前的Mode中没有任何Source, Timer, Observer那么直接退出Runloop

自动释放池什么时候释放?

  1. 在Runloop睡眠之前(KCFRunLoopBeforeWaiting)

在开发中如何使用Runloop? 什么应用场景?

  1. 开启一个常驻线程(让子线程不进入消亡状态, 等待其他线程发来消息, 处理其他事件)
    1.1在子线程中开启一个定时器
    2.1在子线程中进行一些长期监控
  2. 可以控制定时器在特定Mode下执行
  3. 可以让某些事件(行为, 任务)在特定Mode下执行
  4. 可以添加Observer监听RunLoop的状态, 比如监听点击事件的处理(在所有点击事件之前做一些事情)

人已赞赏
iOS文章

iOS-UIKit继承图

2020-3-4 4:08:31

iOS文章

iOS GCD小结

2020-3-4 6:08:35

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索