美文网首页
2019-12-25

2019-12-25

作者: 王家小雷 | 来源:发表于2019-12-25 10:33 被阅读0次
image.png

多线程解决耗时操作示例代码

  • (void)touchesBegan:(NSSet )touches withEvent:(UIEvent)event {

// 直接调用//

[self demo];

//第一种方法

// [self performSelectorInBackground:@selector(demo) withObject:nil];

//第二种方法

[NSThreaddetachNewThreadSelector:@selector(demo) toTarget:selfwithObject:nil];

// 第三种方法

// dispatch_async(dispatch_get_global_queue(0, 0), ^{

// [self demo];

// });

}

pragma mark --解决耗时操作 UI+数据请求 单个

-(void)func1{
// dispatch_async会向dispatch_get_global_queue全局队列去添加新的任务
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//执行耗时操作
//操作执行完成回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
//回到主线程然后执行后续操作 如刷新啥的

    });
});

}

pragma mark --多个耗时操作

-(void)func2{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建分组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
//耗时操作1
NSLog(@"耗时操作1");
});
dispatch_group_async(group, queue, ^{
//耗时操作2
NSLog(@"耗时操作2");
});
//回到主线程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//执行后续操作
});

}

pragma mark --耗时操作

-(void)haoshiFunc{
for (int i=0; i<10000000; i++) {
UILabel lb1 =[[UILabel alloc] init];
lb1.text=[NSString stringWithFormat:@"%d",i
i];
[self.view addSubview:lb1];
}
}

pragma mark --performSelector

-(void)func3{
//在当前线程,延迟一秒执行,响应了oc语言的动态特性:延迟到运行时才绑定方法
[self performSelector:@selector(haoshiFunc) withObject:nil afterDelay:1];
//回到主线程,waitUntilDone:是否将该回调方法执行完在执行后面的代码,如果为YES,就必须等回调方法完成之后才能执行后面的代码,说白了就是阻塞当前的线程,如果是NO,就是不等回调方法结束,不会阻塞当前线程
[self performSelectorOnMainThread:@selector(haoshiFunc) withObject:nil waitUntilDone:NO];
//开辟子线程
[self performSelectorInBackground:@selector(haoshiFunc) withObject:nil];
//在指定线程执行
[self performSelector:@selector(haoshiFunc) onThread:[NSThread currentThread] withObject:nil waitUntilDone:NO];

//如果是带afterDelay的延时函数,会在内部创建一个NStimer,然后在添加到当前的线程的Runloop中,也就是如果当前线程没有开启runloop,f该方法会失效,在子线程中,需要启动runloop(注意调用顺序)  而performSelector:withObject:只是一个单纯的消息发送,和时间没有一点关系。所以不需要添加到子线程的Runloop中也能执行
[self performSelector:@selector(haoshiFunc) withObject:nil afterDelay:1];
[[NSRunLoop currentRunLoop] run];

}

//dispatch_barrier_async
//
//1、问:怎么用GCD实现多读单写?
//这里的dispatch_barrier_sync上的队列要和需要阻塞的任务在同一队列上,否则是无效的。
//从打印上看,任务0-9和任务任务10-19因为是异步并发的原因,彼此是无序的。而由于栅栏函数的存在,导致顺序必然是先执行任务0-9,再执行栅栏函数,再去执行任务10-19。
-(void)func4{
dispatch_queue_t concurrentQueue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

for (NSInteger i = 0; i < 10; i++) {
    
    dispatch_sync(concurrentQueue, ^{
        
        NSLog(@"%zd",i);
    });
}

dispatch_barrier_sync(concurrentQueue, ^{
    
    NSLog(@"barrier");
});

for (NSInteger i = 10; i < 20; i++) {
    
    dispatch_sync(concurrentQueue, ^{
        
        NSLog(@"%zd",i);
    });
}

}
1、怎么创建一个常驻线程?

1、为当前线程开启一个RunLoop(第一次调用 [NSRunLoop currentRunLoop]方法时实际是会先去创建一个RunLoop)
1、向当前RunLoop中添加一个Port/Source等维持RunLoop的事件循环(如果RunLoop的mode中一个item都没有,RunLoop会退出)
2、启动该RunLoop

@autoreleasepool {

    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    
    [runLoop run];
    
}

2、输出下边代码的执行顺序

NSLog(@"1");

dispatch_async(dispatch_get_global_queue(0, 0), ^{

NSLog(@"2");

[self performSelector:@selector(test) withObject:nil afterDelay:10];

NSLog(@"3");

});

NSLog(@"4");

  • (void)test
    {

    NSLog(@"5");
    }
    答案是1423,test方法并不会执行。
    原因是如果是带afterDelay的延时函数,会在内部创建一个 NSTimer,然后添加到当前线程的RunLoop中。也就是如果当前线程没有开启RunLoop,该方法会失效。
    那么我们改成:

dispatch_async(dispatch_get_global_queue(0, 0), ^{

    NSLog(@"2");
    
    [[NSRunLoop currentRunLoop] run];
    
    [self performSelector:@selector(test) withObject:nil afterDelay:10];

    NSLog(@"3");
});

然而test方法依然不执行。
原因是如果RunLoop的mode中一个item都没有,RunLoop会退出。即在调用RunLoop的run方法后,由于其mode中没有添加任何item去维持RunLoop的时间循环,RunLoop随即还是会退出。
所以我们自己启动RunLoop,一定要在添加item后

dispatch_async(dispatch_get_global_queue(0, 0), ^{

    NSLog(@"2");
    
    [self performSelector:@selector(test) withObject:nil afterDelay:10];
    
    [[NSRunLoop currentRunLoop] run];

    NSLog(@"3");
});

3、怎样保证子线程数据回来更新UI的时候不打断用户的滑动操作?

当我们在子请求数据的同时滑动浏览当前页面,如果数据请求成功要切回主线程更新UI,那么就会影响当前正在滑动的体验。
我们就可以将更新UI事件放在主线程的NSDefaultRunLoopMode上执行即可,这样就会等用户不再滑动页面,主线程RunLoop由UITrackingRunLoopMode切换到NSDefaultRunLoopMode时再去更新UI

[self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];

相关文章

网友评论

      本文标题:2019-12-25

      本文链接:https://www.haomeiwen.com/subject/ndzqoctx.html