Runloop 多线程

Runloop 的使命:
1 负责程序不退出
2 负责监听IOS所有的事件,如:触摸,时钟,网络事件

3 如果没有事件发送,会让程序进入休眠状态

Runloop 的两种模式: NSDefaultRunLoopMode+ NSRunLoopCommonModes
NSDefaultRunLoopMode:时钟+网络事件

NSRunLoopCommonModes:用户交互

来看看这段代码~思考下:以下代码的输出是什么?

int main(int argc, char * argv[]) {

    @autoreleasepool {   

    NSLog(@"来了");

     nil 就相当于UIApplication

     int a = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

    NSLog(@"come here");

     return a;

    }

}

想好了吗?揭晓答案了哦~答案是“来了”,你猜对了吗?why?因为UIApplicationMain 有个死循环(runloop循环)

死循环的目的何在?

- 保证去程序不退出

- 负责监听事件,触摸,时钟,网络事件

- 如果没有事件发生,会让程序进入休眠状态(也会消耗cpu的性能)

Runloop 的五种模式

1.UITrackingRunLoopMode UI模式

2.NSDefaultRunLoopMode 默认模式

3.NSRunLoopCommonModes 占位模式相当于 UI模式+ 默认模式

4.UIInitializationRunLoopMode:在刚启动App时进入的第一个Mode,启动完成后不再使用

5.GSEventReceiveRunLoopMode接受系统事件的内部Mode,通常用不到

有这样一个场景 :界面上有个textview,当滚动textview,viewdidload 里的timer就不工作了

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeMethod) userInfo:nil repeats:YES];

 [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];

我们来解释下原因,请看下图

Runloop 多线程_第1张图片

此时的timer在默认模式下,苹果的一贯注重用户体验,所以当滚动textview (用户操作UI)的时候,runloop 会优先处理UI模式下的source。如果这时吧计时器加入到UI模式下,timeMethod 会正常进行,但当用户停止操作UI,那么UI模式下的runloop 就会进行休眠,所以timeMethod也会停止运行。此时我们可以吧timer加入到UI模式+默认模式。这时苹果为了优化,产生了一个NSRunLoopCommonModes 占位模式,但是它并不属于Runloop的五大模式之一。


RunLoop && 多线程

- (void)viewDidLoad {

    [super viewDidLoad];

    NSThread *thread = [[NSThread alloc]initWithBlock:^{

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeMethod) userInfo:nil repeats:YES];

        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

        NSLog(@"来了1");

    }];

    [thread start];

}

- (void)timeMethod {

    NSLog(@"来了2");

    [NSThread sleepForTimeInterval:1];

    NSLog(@"%@",[NSThread currentThread]);

}

2018-05-16 10:05:46.915347+0800 测试完[841:26856] 来了1


原因:线程走了,想要保证线程的命,就要让线程有执行不完的任务,线程就不会释放了~

- (void)viewDidLoad {

    [super viewDidLoad];

    NSThread *thread = [[NSThread alloc]initWithBlock:^{

 NSTime*timer=                    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeMethod) userInfo:nilrepeats:YES];

        [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

       //这个循环只能保证线程的命,但是timeMethod 还是不能调用

       // while(true)

       //{

             //从事件队列中取出事件来处理!!(但是平没有开源这个,但是封装了一个NSRunLoop 来处理这个事件

       // }

       //RunLoop -- 一条线程上面的RunLoop模式是不循环的

        //CFRunLoop -- currentRunLoop ()  第一次获取RunLoop的时候,创建RunLoop

        [[NSRunLoop currentRunLoop]run];//死循环

       NSLog(@"来了1");

    }];

    [thread start];

}

- (void)timeMethod {

    NSLog(@"来了2");

    [NSThread sleepForTimeInterval:1];

    NSLog(@"%@",[NSThread currentThread]);

}

打印结果如下:

2018-05-11 10:06:44.594863+0800 RunloopTest[1300:42091] 来了2

2018-05-11 10:06:45.599998+0800 RunloopTest[1300:42091] {number = 4, name = (null)}

主线程与子线程做的事情都是一样的,唯一的区别是:UI界面在主线上面的;App启动的第一条线程

苹果为啥要吧UI界面放在主线程?为了安全+效率

三,CFRunloop 的一个用法


#import "ViewController.h"


typedef void(^RunloopBlock)(void);


@interface ViewController ()

@property(strong,nonatomic)NSMutableArray *tasks;

@end


@implementation ViewController

//这里在cell 上加载图片的耗时操作没写

/*分析卡顿的原因:

所有的Cell的加载都在主线程的一次Runloop循环中,UI渲染也属于Runloop的事情,但是一次渲染18张图片,渲染太多。导致卡顿

解决思路:一次Runloop循环,只加载一张图片

步骤:

 1.观察(observer)Runloop的循环

 2.一次Runloop循环,加载一张图片

  |-Cell加载图片的方法放到数组里

  |-Runloop循环 一次,就从数组取出一个图片加载

*/

- (void)viewDidLoad {

    [self addRunloopObserver];

    self.tasks = NSMutableArray.array;

    [self addTask:^{

       

    }];

}

- (void)addTask:(RunloopBlock)task{

    //保存任务到数组

    [self.tasks addObject:task];

    if (self.tasks.count == 0) {

        return;

    }

    if (self.tasks.count>18) {

        [self.tasks removeObjectAtIndex:0];

    }

}

- (void)addRunloopObserver{

    //获取当前的runloop

    CFRunLoopRef runloop = CFRunLoopGetCurrent();

    //定义上下文

    /*

     typedef struct {

     CFIndex    version;

     void *    info;

     const void *(*retain)(const void *info);

     void    (*release)(const void *info);

     CFStringRef    (*copyDescription)(const void *info);

     } CFRunLoopObserverContext;

     */

    

    CFRunLoopObserverContext context = {

        0,

        (__bridge void *)self,

        &CFRetain,

        &CFRelease,

        NULL

    };

    

    //创建观察者

    //c 中create new copy 堆区开辟内存空间。需要释放

    CFRunLoopObserverRef  runLoopObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &callback, &context);

    CFRunLoopAddObserver(runloop, runLoopObserver, kCFRunLoopCommonModes);

    //释放

    CFRelease(runloop);

}


void callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){

    ViewController *vc =  (__bridge ViewController *)info;

    if (vc.tasks.count == 0) {

        return;

    }

    RunloopBlock task = vc.tasks.firstObject;

    task();

    [vc.tasks removeObjectAtIndex:0];

};






你可能感兴趣的:(Runloop 多线程)