runloop

何时使用 Run Loop?

仅当在为你的程序创建辅助线程的时候,你才需要显式运行一个 run loop。Run loop 是程序主线程基础设施的关键部分。所以,Cocoa 和 Carbon 程序提供了代码运 行主程序的循环并自动启动 run loop。IOS 程序中 UIApplication 的 run 方法(或 Mac OS X 中的 NSApplication)作为程序启动步骤的一部分,它在程序正常启动的时 候就会启动程序的主循环。类似的,RunApplicationEventLoop 函数为 Carbon 程序 启动主循环。如果你使用 xcode 提供的模板创建你的程序,那你永远不需要自己去显 式的调用这些例程。

对于辅助线程,你需要判断一个 run loop 是否是必须的。如果是必须的,那么 你要自己配置并启动它。你不需要在任何情况下都去启动一个线程的 run loop。比 如,你使用线程来处理一个预先定义的长时间运行的任务时,你应该避免启动 run loop。Run loop 在你要和线程有更多的交互时才需要,比如以下情况:

 使用端口或自定义输入源来和其他线程通信  使用线程的定时器  Cocoa 中使用任何 performSelector...的方法  使线程周期性工作

 

如果你决定在程序中使用 run loop,那么它的配置和启动都很简单。和所有线程 编程一样,你需要计划好在辅助线程退出线程的情形。让线程自然退出往往比强制关 闭它更好。关于更多介绍如何配置和退出一个 run loop,参阅”使用 Run Loop 对象” 的介绍。

 

作用

  • 使程序一直运行并接收用户的输入
  • 决定程序在何时处理哪些事件
  • 调用解耦(Message Queue)
  • 节省CPU时间(当程序启动后,什么都没有执行的话,就不用让CPU来消耗资源来执行,直接进入睡眠状态)

模式

  • RunLoop 在同一段时间只能且必须在一种特定的模式下运行
  • 如果要更换 Mode,必须先停止当前的 Loop,然后再重新启动 Loop
  • Mode 是保证滚动流畅的关键

  • NSDefaultRunLoopMode:默认状态、空闲状态

  • UITrackingRunLoopMode:滚动模式
  • UIInitializationRunLoopMode:私有的,App启动时
  • NSRunLoopCommonModes:默认包含1,2两种模式

模拟 RunLoop 实现

void runEvent(int num){
    if (num == 1){
        NSLog(@"按钮点击事件");
    }

    if (num == 2) {
        NSLog(@"scrollView拖动事件");
    }

}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        //NSLog(@"Hello, World!");

        while (YES) {
            NSLog(@"请输入选择项,0表示退出");

            int num =0;

            scanf("%d",&num);

            if (num==0) {
                break;
            }else{
                runEvent(num);
            }
        }
    }
    return 0;
}

运行循环与时钟

@interface ViewController ()

@property(nonatomic,strong)NSTimer *timer;

@property(nonatomic,assign)int count;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];

    //需要将其加入到运行循环里面才能执行
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)demo{
    self.count++;

    NSLog(@"hello===%d",self.count);
}
  • 将时钟添加到其他线程工作
@interface ViewController ()

@property(nonatomic,strong)NSTimer *timer;

@property(nonatomic,assign)int count;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //1.创建一个子线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil];

    [thread start];
}

/**
 1.在子线程中,如下事件是不会开启的

 1.定时源事件
 2.输入源事件
 3.网络下载
 */
- (void)threadRun{
    NSLog(@"%s",__func__);

    self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];

    //需要将其加入到运行循环里面才能执行
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

    //1.需要我们自己手工开启我们子线程的NSRunLoop
    CFRunLoopRun();
}

- (void)demo{

    self.count++;

    if (self.count==5) {
        NSLog(@"---停止子线程运行循环---");

        //停止运行循环,这样子线程才能销毁
        CFRunLoopStop(CFRunLoopGetCurrent());
    }

    NSLog(@"hello===%d",self.count);
}

注意:主线程的运行循环是默认启动的,但是子线程的运行循环是默认不工作的,子线程的运行循环需要我们自己手工开启和关闭,只有自己手工关闭,这样才能够保证线程执行完毕后,子线程自动被销毁

posted @ 2015-12-30 18:21  hao_coming  阅读(411)  评论(0编辑  收藏  举报