RunLoop深度探究以及有关面试题

首先什么是runloop?

顾名思义,运行循环,在程序运行中做一些事情。下面就是一个runloop,让程序不会马上退出,保持运行状态

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

RunLoop的基本作用:

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

iOS中有2套API来访问和使用RunLoop,Foundation中的NSRunLoop和Core Foundation的CFRunLoopRef,NSRunLoop是基于CFRunLoopRef的一层OC包装,CFRunLoopRef是开源的,https://opensource.apple.com/tarballs/CF/在这里可以查看。

RunLoop与线程的关系

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
  • 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
  • RunLoop会在线程结束时销毁
  • 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

Core Foundation中关于RunLoop的5个类

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

  • CFRunLoopModeRef代表RunLoop的运行模式
  • 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
  • RunLoop启动时只能选择其中一个Mode,作为currentMode
  • 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
  • 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出

常见的两种model,kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行,UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

RunLoop的运行逻辑

  1. 通知Observers:进入Loop
  2. 通知Observers:即将处理Timers
  3. 通知Observers:即将处理Sources
  4. 处理Blocks
  5. 处理Source0(可能会再次处理Blocks)
  6. 如果存在Source1,就跳转到第8步
  7. 通知Observers:开始休眠(等待消息唤醒)
  8. 通知Observers:结束休眠(被某个消息唤醒) 处理Timer 或者处理GCD Async To Main Queue 或者 处理Source1
  9. 处理Blocks
  10. 根据前面的执行结果,决定如何操作: 回到第02步 或者 退出Loop
  11. 通知Observers:退出Loop

runloop运行原理:是从用户态转到了内核态,在内核态等待消息,有消息就唤醒线程,没有就让线程休眠。

下面是runloop一个实际应用,线程保活,为了方便看是否销毁了线程,写了个FGThread继承了NSThread,实际上直接nsthread就可以了。

@interface FGPermenantThread : NSObject

- (void)executeWith:(void (^)(void))task;

- (void)stop;

@end
@interface FGThread : NSThread

@end
@implementation FGThread

- (void)dealloc{
    NSLog(@"%s",__func__);
}

@end
@interface FGPermenantThread ()

@property (strong, nonatomic) FGThread *thread;
@property (assign ,nonatomic) BOOL isStopped;
@end
@implementation FGPermenantThread

- (instancetype)init{
    if(self = [super init]){
        self.isStopped = NO;
        __weak typeof(self) weakSelf = self;
        self.thread = [[FGThread alloc] initWithBlock:^{
            NSLog(@"----begin");
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            if(weakSelf.isStopped && weakSelf){
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
            NSLog(@"----end");
        }];
        [self.thread start];
    }
    return self;
}

- (void)dealloc{
    NSLog(@"%s",__func__);
    
    [self stop];
}

#pragma mark -- public methods
- (void)executeWith:(void (^)(void))task{
    if(!self.thread || !task) return;
    [self performSelector:@selector(__execute:) onThread:self.thread withObject:task waitUntilDone:NO];
}

- (void)stop{
    if(!self.thread) return;
    [self performSelector:@selector(__stop) onThread:self.thread withObject:nil waitUntilDone:YES];
}

#pragma mark -- private methods
- (void)__stop{
    self.isStopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.thread = nil;
}

- (void)__execute:(void (^)(void))task{
    task();
}
@end

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值