runloop

oc中报错经常看到最多的除了栈地址和运行时就属runloop 最多了,先说说自己现在的理解(白话文):

我们平时一个线程执行完任务就会这个线程就会销毁,或被系统调配去干别的事情了。重新开启线程很耗性能的。那么如何让这个线程一直停留,有事情的时候处理事情没事情的时候休眠,并可随时唤醒这个线程。线程和runloop 是一一对应的,系统中,线程和runloop是保存在字典中的,我们可以通过线程这个key取出相对应的runloop。oc中的runloop 分为CFRunloopRef 和NSRunloop, CFRunloopRef 是基于coreFoundation 框架下的,比较底层。NSRunloop 是对CFRunloop 的封装。runloop 自己不能创建,只能通过调用currentRunloop 去获取。

Thread包含一个CFRunLoop,一个CFRunLoop包含一种CFRunLoopMode,mode包含CFRunLoopSource,CFRunLoopTimer和CFRunLoopObserver。mode 下的这些item 是mode 间共用的。
oc中的我们常用的model :
  • NSDefaultRunLoopMode:默认,空闲状态
  • UITrackingRunLoopMode:ScrollView滑动时
  • UIInitializationRunLoopMode:启动时
  • NSRunLoopCommonModes:Mode集合 Timer计时会被scrollView的滑动影响的问题-可以通过将timer添加到NSRunLoopCommonModes来解决
    commonmodes 包含前两种。

主线程的runloop 默认是一直启动的,只是根据不同的时刻进行mode 切换。model 切换相对来说是耗性能的。并且model 的切换必须等待上一个runloop循环执行完成才可以。所以我们的在主线程的定时器或者延时几秒执行的任务有可能不准。而且当table滚动的时候会造成timer不回调。

CFRunLoopSourceRef : source 分为source0 和source1,source0 只包含一个函数指针,不能主动触发事件,可以手动调用CFRunloopSourceSignal(source),将这个source标记为待处理,然后调用CFRunloopSourceWakeup(runloop)来唤醒Runloop,souce1 则包含了一个machport 和一个函数指针,通过内核或其它线程互相发送消息,其可以主动唤醒事件。所以我认为我们将machport 添加给runloop 应该就是添加到source1了。

CFRunloopTimerRef: 这个timer 其实就是NSTimer,NSTimer是对其进行了封装。主要用于runloop的其包含一个时间长度和一个回调,当其加入runloop时,runloop会注册相对应的时间点,当时间到时,runloop会被唤醒以执行那个回调。

CFRunloopObserRef: 每个observer 都包含了一个回调,当runloop 的状态发生变化时,观察者就能通过回调接收这个变化。比如我们即将进入runloop,开始处理timer,即将休眠等这些状态都会被observer接收。

实际应用中,比如我们想封装个sdk,想让某个线程一直高效的处理某件事,那么我们就用到了,如何不让timer 受影响,那么我们把这个timer放到commonmode里,这样主线程就不会因为切换mode 受影响了。还有那么我们如果想在tableViewCell 中延迟加载图片,那么我们放到runloop 的defalutModel里就好了。既然主线程有时候会受影响,那么我们可以在子线程的runloop中做一些事情啊,比如把定时器挪到子线程。当后台一直需要处理任务,可以开启个子线程的runloop去做啊。有时候我们分析错误,也可以用的到(AutoReleasPool也跟runloop有关系,以后再说吧)。

上面说的比较表层,个人理解,有错误可以一起讨论,以后深入了再补充,上代码:

1、滚动延迟加载图片

[self.headView performSelector:@selector(setImage:) withObject:img afterDelay:0 inModes:@[NSRunLoopCommonModes]];

2、定时器 timer

UI线程定时器

NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doSometing) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

子线程定时器

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerTest) userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
});

gcd定时器

__block int timeout = 99;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{

    NSLog(@"GDD timer %@",[NSThread currentThread]);
    NSLog(@"timeutwai%d",timeout);
    timeout --;
    if ( timeout < 0) {

        dispatch_source_cancel(timer);
    }
});
dispatch_resume(timer);

注:用timer注意销毁,否则容易内存泄露。当一个timer被schedule的时候,timer会持有target对象,NSRunloop对象会持有timer。只有当invalidate的时候,NSRunloop 对象会是释放timer,timer会释放对target的持有,所以别忘了invalidate。所以尽量用GCD, 精度高,属于线程级的操作,系统自动释放,不用关心runloop引起的问题。
3、如何后台开启一个线程呢

NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(backgroundMethod) object:nil];
[thread start];
 - (void)backgroundMethod {
NSLog(@"thread %s",__func__); 
[[NSRunLoop currentRunLoop]  addPort:[NSPort new] forMode:NSDefaultRunLoopMode]; 
[[NSRunLoop currentRunLoop] run];  }

4、封装一个线程用于我们自己写的sdk

+(NSThread*)customThread {
static NSThread * customThread = nil;

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    customThread = [[NSThread alloc]initWithTarget:self selector:@selector(threadEntryPoint:) object:nil];
    [customThread start];
});
return  customThread;

}
+ (void)threadEntryPoint:(id)object {

[[NSThread currentThread]setName:@"customThread"];
NSRunLoop * runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runloop run];}

有时间了再补充吧~~~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值