1.runloop是什么?
runloop 是一个运行循环(死循环);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 该句代码默认会开启一个主线程
例证:
2.runloop有什么用?
(1)保证线程不退出
我们知道线程结束的条件是,线程中未有未执行的任务
所以为了保证主线程运转,执行 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 该句代码创建了一个runloop
注意:(1)主线程:是指app启动时第一个创建的线程,在该线程中才能执行UI有关操作,除此之外其实和其它子线程没有区分,(这也是为什么UI作为属性时,使用的是nonatomic)
(2)负责监听事件:触摸事件、时钟事件、网络事件:
案例 当滑动其他页面时,顶部轮播图不会轮播
3.runloop和多线程
(1) 线程默认是不开启runloop的,线程不被cpu收回的前提是线程中有未执行完的任务
该线程开启后,不会执行eat方法 因为
(1)。线程已经死掉了(oc对象保留,但线程已经死掉了,因为未有未完成的任务(runloop没有开启))
正确的方法是:
4.runloop 性能优化(加载大图时,渲染图片像素较高)
问题:当用tableview显示高清图片时,如果一屏的高清图片过多,导致在一个runloop中渲染图片的时间过多,进而不能及时响应用户的滑动操作(在某个项目 *觅 中就出行过这种情况)导致滑动时显示出卡顿
注意:渲染图片属于runloop要做的事,滑动tableview也属于runloop要处理的事情,在一次性循环中,runloop既要渲染大量的高清图片又要响应拖拽事件,因此就会显得滑动卡顿
解决思路:让每个runloop中只渲染一张图片,这样就不会导致渲染很多图片后,才响应用户的滑动动作
步骤
1.添加观察runloop各个状态的观察者
2.把添加图片的代码加到一个数组中
3.添加一个间隔0.001秒的定时器,保证线程每个0.001秒就被唤醒 然后执行渲染图片的任务,因为时间间隔非常短,因此加载图片非常快
4.定时器进入休眠前执行渲染某一张图片的任务
注意点:
(1)观察runloop 要用到 CoreFoundation 框架中的 CFRunloop。 注意啦:CF是由C语言实现的,而不是Objective-C,所以如果用到了CF,就需要手动管理内存,ARC是无能为力的;
CF中遇到create、new、copy 要记得手动释放内存
例如:
(2)C语言中只能用函数指针来完成回调,OC中当然有很多方式:通知、block、代理、kvo
(3)桥接
先来说说「Core Foundation」(以下简称CF)的历史吧。当年乔布斯被自己创办的公司驱逐后,成立了「NeXT Computer」,其实做的还是老本行:卖电脑,但依旧不景气。好在NeXTSTEP系统表现还不错,亏损不至于太严重。正好此时苹果的市场份额大跌,急需一个新的操作系统,结果大家都知道了,乔布斯借此收购,重新回到了苹果。
这里就牵扯到了一个问题,如何让旧有的系统(Mac OS 9)和NeXTSTEP合成为一个新系统?这就需要一个更为底层的核心库可以供Mac Toolbox和OPENSTEP双方调用。CF就这么诞生了。
CF是由C语言实现的,而不是Objective-C,所以如果用到了CF,就需要手动管理内存,ARC是无能为力的。当然因为CF和Foundation之间的友好关系,它们之间的管理权也是可以移交的,这个后面再说。
Core Foundation框架(CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。下面列举该框架支持进行管理的数据以及可提供的服务:
群体数据类型 (数组、集合等)
程序包
字符串管理
日期和时间管理
原始数据块管理
偏好管理
URL及数据流操作
线程和RunLoop
端口和soket通讯
Core Foundation框架和Foundation框架紧密相关,它们为相同功能提供接口,但Foundation框架提供Objective-C接口。如果您将Foundation对象和Core Foundation类型掺杂使用,则可利用两个框架之间的 “toll-free bridging”。所谓的Toll-free bridging是说您可以在某个框架的方法或函数同时使用Core Foundatio和Foundation 框架中的某些类型。很多数据类型支持这一特性,其中包括群体和字符串数据类型。每个框架的类和类型描述都会对某个对象是否为 toll-free bridged,应和什么对象桥接进行说明。
CoreFoundation 是C语言构成 而Foundation 是用OC把CoreFoundation进行包装了一遍
CoreFoundation 和 Foundation 之间相互转换就用到了桥接
桥接分为三种模式
第一种桥接方式 __bridge transfers a pointer between Objective-C and Core Foundation with no transfer of ownership.(这种方式只是传递了指针,而没有传递对象的所有权,也就是如果str销毁了,strC也不能用了)
CFStringRef strC = nil;
{
NSString *str = [NSString stringWithFormat:@"123"];
strC = (__bridge CFStringRef)(str);
}
NSLog(@"%@ ",strC);
第二种桥接方式 __bridge_retained or CFBridgingRetain casts an Objective-C pointer to a Core Foundation pointer and also transfers ownership to you.You are responsible for calling CFRelease or a related function to relinquish ownership of the object.(这种方式只是用于OC对象转换为CF类型,它会将对象的所有权转移给strC,也就是说,即便str释放了,strC也可以使用
注意 在ARC条件下,如果使用__bridge_retained桥接,那么strC必须手动释放,因为桥接的时候已经将对象的所有转移给了strC,而C语言的东西是不归ARC管理的
NSString *str = [NSString stringWithFormat:@"123"];
CFStringRef strC = (__bridge_retained CFStringRef)str;
//CFStringRef StrC = CFBridgingRetain(str); 这一句和上面一句的效果一样
NSLog(@"%@ %@",str,strC);
CFRelease(strC);
第三种桥接方式 __bridge_transfer or CFBridgingRelease moves a non-Objective-C pointer to Objective-C and also transfers ownership to ARC.ARC is responsible for relinquishing ownership of the object.这种方式只是用于CF对象转换为OC类型,它会将对象的所有权转移给str,也就是说,即便strC释放了,str也可以使用
//注意 在ARC条件下,如果使用__bridge_transfer桥接,那么strC不用手动释放,因为__bridge_transfer会自动释放strC的
CFStringRef strC = CFStringCreateWithCString(nil, "456", kCFStringEncodingASCII);
NSString *str = (__bridge_transfer NSString*) (strC);
NSString *str = CFBridgingRelease(strC); //这一句和上面一句的效果一样
NSLog(@"%@ %@",str,strC);
CFRelease(strC);
总结
runloop和线程之间的关系
* 每个线程有且仅有一个对应的runloop,子线程的runloop默认是没有开启的,但是主线程的runloop默认是开启的
*runloop是懒加载,获得runloop等于开启runloop 例如: [NSRunLoop currentRunLoop]
*runloop有5中mode(模式),但是主要的是两种模式:NSDefaultRunLoopMode(默认模式)和UITrackingUIRunLoopMode(界面追踪模式,也就是页面滑到时主线程会切换到UITrancking模式),要注意
**runloop启动时,只能有一种模式,要切换到另外一种模式,runloop要先退出再开启。
**每种模式都有source、定时器、observe三种触发模式,并且不同模式下,三种触发是相互隔离的,也就是说,比如定时器1加到NSDefault这种模式下了,那么当runloop启动时运行在UITrancking模式下事,就不会接收到定时器1的触发了
例如:NStimer定时滑动的问题,如果NStimer只加入到NSDefault模式下,那么当页面滑动时,主线程的runloop从default模式退出,开启UITrancking模式,但是NStimer并没有添加到UITrancking模式下,所以在滑动页面时,就收不到滑动的触发
参考