OC中的多线程

2019-11-07  本文已影响0人  iOSWater

OC中多线程根据封装程度可以分为三个层次:NSThread、GCD和NSOperation,另外由于OC兼容C语言,因此仍然可以使用C语言的POSIX接口来实现多线程,只需引入相应的头文件:#include <pthread.h>。

NSThread

NSThread是封装程度最小最轻量级的,使用更灵活,但要手动管理线程的生命周期、线程同步和线程加锁等,开销较大;

下面以在一个UIViewController中为例展示NSThread的使用方法:

- (void)viewDidLoad {
    [super viewDidLoad];

    /** NSThread静态工具方法 **/
    /* 1 是否开启了多线程 */
    BOOL isMultiThreaded = [NSThread isMultiThreaded];
    /* 2 获取当前线程 */
    NSThread *currentThread = [NSThread currentThread];
    /* 3 获取主线程 */
    NSThread *mainThread = [NSThread mainThread];
    NSLog(@"main thread");
    /* 4 睡眠当前线程 */
    /* 4.1 线程睡眠5s钟 */
    [NSThread sleepForTimeInterval:5];
    /* 4.2 线程睡眠到指定时间,效果同上 */
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
    /* 5 退出当前线程,注意不要在主线程调用,防止主线程被kill掉 */
    //[NSThread exit];     NSLog(@"main thread");

    /** NSThread线程对象基本创建,target为入口函数所在的对象,selector为线程入口函数 **/
    /* 1 线程实例对象创建与设置 */
    NSThread *newThread= [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    /* 设置线程优先级threadPriority(0~1.0),即将被抛弃,将使用qualityOfService代替 */
    newThread.threadPriority = 1.0;
    newThread.qualityOfService = NSQualityOfServiceUserInteractive;
    /* 开启线程 */
    [newThread start];
    /* 2 静态方法快速创建并开启新线程 */**detach分派**
    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"block run...");
    }];

    /** NSObejct基类隐式创建线程的一些静态工具方法 **/
    /* 1 在当前线程上执行方法,延迟2s */
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    /* 2 在指定线程上执行方法,不等待当前线程 */
    [self performSelector:@selector(run) onThread:newThread withObject:nil waitUntilDone:NO];
    /* 3 后台异步执行函数 */
    [self performSelectorInBackground:@selector(run) withObject:nil];
    /* 4 在主线程上执行函数 */
    [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
}

- (void)run {
    NSLog(@"run...");
}

GCD

GCD(Grand Central Dispatch),对线程操作进行了封装,加入了很多新的特性,内部进行了效率优化,提供了简洁的C语言接口,使用更加简单高效,也是苹果推荐的方式。

同步dispatch_sync与异步dispatch_async任务派发
串行队列与并发队列dispatch_queue_t
dispatch_once_t只执行一次
dispatch_after延后执行

dispatch_queue_t 操作队列主要有两种,并发队列和串行队列,它们的区别上面已经提到,具体创建的方法很简单,要提供两个参数,一个是标记该自定义队列的唯一字符串,另一个是指定串行队列还是并发队列的宏参数:

/* 创建一个并发队列 */
dispatch_queue_t concurrent_queue = dispatch_queue_create("demo.gcd.concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
/* 创建一个串行队列 */
dispatch_queue_t serial_queue = dispatch_queue_create("demo.gcd.serial_queue", DISPATCH_QUEUE_SERIAL);

另外GCD还提供了几个常用的全局队列以及主队列,获取方法如下:

// 获取主队列(在主线程上执行)
dispatch_queue_t main_queue = dispatch_get_main_queue();
// 获取不同优先级的全局队列(优先级从高到低)
dispatch_queue_t queue_high = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t queue_default = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue_low = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t queue_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

dispatch_once_t 这个函数控制指定代码只会被执行一次,常用来实现单例模式,这里以单例模式实现的模板代码为例展示dispatch_once_t的用法,其中的实例化语句只会被执行一次:

+ (instancetype *)sharedInstance {
    static dispatch_once_t once = 0;
    static id sharedInstance = nil;
    dispatch_once(&once, ^{
        // 只实例化一次
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

dispatch_after 通过该函数可以让要提交的任务在从提交开始后的指定时间后执行,也就是定时延迟执行提交的任务,使用方法很简单:

    // 定义延迟时间:3s
    dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
    dispatch_after(delay, dispatch_get_main_queue(), ^{
        // 要执行的任务...
    });

dispatch_group_t
组调度可以实现等待一组操作都完成后执行后续操作,典型的例子是大图片的下载,例如可以将大图片分成几块同时下载,等各部分都下载完后再后续将图片拼接起来,提高下载的效率。使用方法如下:

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 */ });
dispatch_group_async(group, queue, ^{ /*操作2 */ });
dispatch_group_async(group, queue, ^{ /*操作3 */ }); 
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 后续操作... });

同步代码到主线程

对于UI的更新代码,必须要在主线程上执行才会及时有效,当当前代码不在主线程时,需要将UI更新的部分代码单独同步到主线程,同步的方法有三种,可以使用NSThread类的performSelectorOnMainThread方法或者NSOperationQueue类的mainQueue主队列来进行同步,但推荐直接使用GCD方法:

dispatch_async(dispatch_get_main_queue(), ^{
        // UI更新代码...
    });

NSOperation

NSOperation是基于GCD的一个抽象基类,将线程封装成要执行的操作,不需要管理线程的生命周期和同步,但比GCD可控性更强,
例如可以加入操作依赖(addDependency)、设置操作队列最大可并发执行的操作个数(setMaxConcurrentOperationCount)、取消操作(cancel)等。NSOperation作为抽象基类不具备封装我们的操作的功能,需要使用两个它的实体子类:NSBlockOperation和NSInvocationOperation,或者继承NSOperation自定义子类。

NSBlockOperation和NSInvocationOperation用法的主要区别是:前者执行指定的方法,后者执行代码块,相对来说后者更加灵活易用。NSOperation操作配置完成后便可调用start函数在当前线程执行,如果要异步执行避免阻塞当前线程则可以加入NSOperationQueue中异步执行。他们的简单用法如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    /* NSInvocationOperation初始化 */
    NSInvocationOperation *invoOpertion = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    [invoOpertion start];

    /* NSBlockOperation初始化 */
    NSBlockOperation *blkOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation");
    }];
    [blkOperation start];
}

- (void)run {
    NSLog(@"NSInvocationOperation");
}

另外NSBlockOperation可以后续继续添加block执行块,操作启动后会在不同线程并发的执行这些执行快:

/* NSBlockOperation初始化 */
NSBlockOperation *blkOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"NSBlockOperationA");
}];
[blkOperation addExecutionBlock:^{
    NSLog(@"NSBlockOperationB");
}];
[blkOperation addExecutionBlock:^{
    NSLog(@"NSBlockOperationC");
}];
[blkOperation start];

2017-04-04 11:27:02.805 SingleView[12008:3666657] NSBlockOperationB
2017-04-04 11:27:02.805 SingleView[12008:3666742] NSBlockOperationC
2017-04-04 11:27:02.805 SingleView[12008:3666745] NSBlockOperationA

另外说了NSOperation的可控性比GCD要强,其中一个非常重要的特性是可以设置各操作之间的依赖,即强行规定操作A要在操作B完成之后才能开始执行,成为操作A依赖于操作B:

/* 获取主队列(主线程) */
NSOperationQueue *queue = [NSOperationQueue mainQueue];
/* 创建a、b、c操作 */
NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"OperationC");
}];
NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"OperationA");
}];
NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"OperationB");
}];
/* 添加操作依赖,c依赖于a和b,这样c一定会在a和b完成后才执行,即顺序为:A、B、C */
[c addDependency:a];
[c addDependency:b];
/* 添加操作a、b、c到操作队列queue(特意将c在a和b之前添加) */
[queue addOperation:c];
[queue addOperation:a];
[queue addOperation:b];

NSBlockOperation和NSInvocationOperation可以满足多数情况下的编程需求,如果需求特殊则需要继承NSOperation类自定义子类来更加灵活的实现。 ***

问题:什么是线程?它与进程有什么区别?为什么要使用多线程?

线程是指程序在执行过程中,能够执行程序代码的一个执行单元。线程主要有四种状态:运行、就绪、挂起、结束。

进程是指一段正在执行的程序。而线程有时候也被称为轻量级进程,是程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)及一些进程级的资源(例如打开的文件),但是各个线程拥有自己的栈空间,进程与线程的关系如下图所示:

image

在操作系统级别上,程序的执行都是以进程为单位的,而每个进程中通常都会有多个线程互不影响地并发执行,那么为什么要使用多线程呢?其实,多线程的使用为程序研发带来了巨大的便利,具体而言,有以下几个方面的内容:

使用多线程可以减少程序的响应时间。在单线程(单线程指的是程序执行过程中只有一个有效操作的序列,不同操作之间都有明确的执行先后顺序)的情况下,如果某个操作很耗时,或者陷入长时间的等待(如等待网络响应),此时程序将不会响应鼠标和键盘等操作,使用多线程后,可以把这个耗时的线程分配到一个单独的线程去执行,使得程序具备了更好的交互性。
与进程相比,线程的创建和切换开销更小。由于启动一个新的线程必须给这个线程分配独立的地址空间,建立许多数据结构来维护线程代码段、数据段等信息,而运行于同一进程内的线程共享代码段、数据段,线程的启动或切换的开销比进程要少很多。同时多线程在数据共享方面效率非常高。
多CPU或多核计算机本身就具有执行多线程的能力,如果使用单个线程,将无法重复利用计算机资源,造成资源的巨大浪费。因此在多CPU计算机上使用多线程能提高CPU的利用率。
使用多线程能简化程序的结构,使程序便于理解和维护。一个非常复杂的进程可以分成多个线程来执行。

问题: 列举Cocoa中常见的几种多线程的实现,并谈谈多线程安全的几种解决办法,一般什么地方会用到多线程?

问的是三个层次的多线程编程实现;线程锁的使用;

只在主线程刷新访问UI
如果要防止资源抢夺,得用synchronized进行加锁保护
如果异步操作要保证线程安全等问题, 尽量使用GCD(有些函数默认 就是安全的)

问题: OC中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么? ***

问题: 在iphone上有两件事情要做,请问是在一个线程里按顺序做效率高还是两个线程里做效率高?为什么?

这里的效率高指的是时间上效率高,也就是希望在最短的时间内完成所有任务,两件事情按顺序做意味着串行执行,第二件事情要等待第一件事情结束后才可开始,效率相对很低。但如果利用两个线程让两件事情能够并发执行,则时间上效率会大大提高。 ***

问题: runloop是什么?在主线程中的某个函数里调用了异步函数,怎样block当前线程,且还能响应当前线程的timer事件,touch事件等? ***

问题: 对比在OS X和iOS中实现并发性的不同方法。

在iOS中有三种方法可以实现并发性:threads、dispatch queues和operation queues。

threads的缺点是开发者要自己创建合适的并发解决方案,要决定创建多少线程并且要根据情况动态调整线程的数量。并且应用还要为创建和维护线程承担主要代价, 因此OS X和IOS系统并不是依靠线程来解决并发问题的,而是采用了异步设计的途径。

其中一个开启异步任务的技术是GCD(Grand Central Dispatch),让系统层次来对线程进行管理。开发者要做的就是定义要执行的任务并将之添加到合适的dispatch分派队列。GCD会负责创建需要的线程并安排任务在这些线程上运行。所有的dispatch队列都是先进先出(FIFO)队列结构,所以任务的执行顺序是和添加顺序是一致的。

operation queues和并发dispatch队列一样都是通过Cocoa框架的NSOperationQueue类实现的,不过它并不一定是按照先进先出的顺序执行任务的,而是支持创建更复杂的执行顺序图来管理任务的执行顺序。 ***

问题: 操作队列(NSOperation queue)是什么?解释NSOperationQueue串行操作队列和并行操作队列的区别。 ***

问题: 用户下载一个图片,图片很大,需要分成很多份进行下载,使用GCD应该如何实现?使用什么队列?

使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理的block。

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 */ });
dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
dispatch_group_async(group, queue, ^{ /*加载图片3 */ }); 
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合并图片 });

问题:Dispatch_barrier_(a)sync的作用?
通过Dispatch_barrier_async添加的操作会暂时阻塞当前队列,即等待前面的并发操作都完成后执行该阻塞操作,待其完成后后面的并发操作才可继续。可以将其比喻为一座霸道的独木桥,是并发队列中的一个并发障碍点,临时阻塞并独占.

可见使用Dispatch_barrier_async可以实现类似dispatch_group_t组调度的效果,同时主要的作用是避免数据竞争,高效访问数据。

/* 创建并发队列 */
dispatch_queue_t concurrentQueue = dispatch_queue_create("test.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
/* 添加两个并发操作A和B,即A和B会并发执行 */
dispatch_async(concurrentQueue, ^(){
    NSLog(@"OperationA");
});
dispatch_async(concurrentQueue, ^(){
    NSLog(@"OperationB");
});
/* 添加barrier障碍操作,会等待前面的并发操作结束,并暂时阻塞后面的并发操作直到其完成 */
dispatch_barrier_async(concurrentQueue, ^(){
    NSLog(@"OperationBarrier!");
});
/* 继续添加并发操作C和D,要等待barrier障碍操作结束才能开始 */
dispatch_async(concurrentQueue, ^(){
    NSLog(@"OperationC");
});
dispatch_async(concurrentQueue, ^(){
    NSLog(@"OperationD");
});

2017-04-04 12:25:02.344 SingleView[12818:3694480] OperationB
2017-04-04 12:25:02.344 SingleView[12818:3694482] OperationA
2017-04-04 12:25:02.345 SingleView[12818:3694482] OperationBarrier!
2017-04-04 12:25:02.345 SingleView[12818:3694482] OperationD
2017-04-04 12:25:02.345 SingleView[12818:3694480] OperationC

问题: 用过NSOperationQueue吗?如果用过或者了解的话,为什么要使用 NSOperationQueue?实现了什么?简述它和GCD的区别和类似的地方(提示:可以从两者的实现机制和适用范围来述)。

使用NSOperationQueue用来管理子类化的NSOperation对象,控制其线程并发数目。GCD和NSOperation都可以实现对线程的管理,区别是NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中使用NSOperation的优点是 NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化 NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会使代码更为易读,建议在简单项目中使用。

GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装
GCD只支持FIFO的队列,NSOperationQueue可以很方便地调整执行顺 序、设置最大并发数量
NSOperationQueue可以在轻松在Operation间设置依赖关系,而GCD 需要写很多的代码才能实现
NSOperationQueue支持KVO,可以监测operation是否正在执行 (isExecuted)、是否结束(isFinished),是否取消(isCanceld) 5> GCD的执行速度比NSOperationQueue快 任务之间不太互相依赖:GCD 任务之间有依赖\或者要监听任务的执行情况:NSOperationQueue

问题: 在使用GCD以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?

使用block是要注意,若将block做函数参数时,需要把它放到最后,GCD是Grand Central Dispatch,是一个对线程开源类库,而Block是闭包,是能够读取其他函数内部变量的函数。

问题: 在项目什么时候选择使用GCD,什么时候选择 NSOperation?

项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现多线程支持,而接口简单,建议在复杂项目中使用。

项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。

问题:对于a,b,c三个线程,如何使用用NSOpertion和NSOpertionQueue实现执行完a,b后再执行c?

可以通过NSOpertion的依赖特性实现该需求,即让c依赖于a和b,这样只有a和b都执行完后,c才可以开始执行:

/* 获取主队列(主线程) */
NSOperationQueue *queue = [NSOperationQueue mainQueue];
/* 创建a、b、c操作 */
NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Operation C Start!");
    // ... ...
}];
NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Operation A Start!");
    [NSThread sleepForTimeInterval:3.0];
    NSLog(@"Operation A Done!");
}];
NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Operation B Start!");
    [NSThread sleepForTimeInterval:3.0];
    NSLog(@"Operation B Done!");
}];
/* 添加操作依赖,c依赖于a和b */
[c addDependency:a];
[c addDependency:b];
/* 添加操作a、b、c到操作队列queue(特意将c在a和b之前添加) */
[queue addOperation:c];
[queue addOperation:a];
[queue addOperation:b];

2017-03-18 13:51:37.770 SingleView[15073:531745] Operation A Start!
2017-03-18 13:51:40.772 SingleView[15073:531745] Operation A Done!
2017-03-18 13:51:40.775 SingleView[15073:531745] Operation B Start!
2017-03-18 13:51:43.799 SingleView[15073:531745] Operation B Done!
2017-03-18 13:51:43.800 SingleView[15073:531745] Operation C Start!

问题:GCD内部是怎么实现的?

iOS和OS X的核心是XNU内核,GCD是基于XNU内核实现的
GCD的API全部在libdispatch库中
GCD的底层实现主要有Dispatch Queue和Dispatch Source
Dispatch Queue :管理block(操作)
Dispatch Source :处理事件

问题:既然提到GCD,那么问一下在使用GCD以及 block 时要注意些什么?它们两是一回事儿么? block 在 ARC 中和传统的 MRC 中的行为和用法有 没有什么区别,需要注意些什么?

Block的使用注意:

block的内存管理
防止循环retian
非ARC(MRC):__block
ARC:__weak__unsafe_unretained

问题:下面代码有什么问题?

int main(int argc, const char * argv[]) {
    NSLog(@"1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    NSLog(@"3");
    return 0;
}

main函数中第二句代码在主线程上使用了dispatch_sync同步向主线程派发任务,而同步派发要等到任务完成后才能返回,阻塞当前线程。也就是说执行到此处,主线程被阻塞,同时又要等主线程执行完成该任务,造成主线程自身的等待循环,也就是死锁。程序运行到此处会崩溃。将dispatch_sync改为dispatch_async异步派发任务即可避免死锁,或者将任务派发到其他队列上而不是主队列。

问题: 简单介绍下NSURLConnection类,以及+sendSynchronousRequest:returningResponse:error:与– initWithRequest:delegate:两个方法的区别?

NSURLConnection主要用于网络链接请求,提供了异步和同步两种请求方式,异步请求会新创建一个线程单独用于之后的下载,不会阻塞当前调用的线程;同步请求会阻塞当前调用线程,等待它下载结束,如果在主线程上进行同步请求则会阻塞主线程,造成界面卡顿。

+sendSynchronousRequest:returningResponse:error:是同步请求数据,会阻塞当前的线程,直到request返回response;

–initWithRequest:delegate:是异步请求数据,不会足阻塞当前线程,当数据请求结束后会通过代理回到主线程,并通知它委托的对象。

问题: UIKit类要在哪一个应用线程上使用?

UIKit的界面类只能在主线程上使用,对界面进行更新,多线程环境中要对界面进行更新必须要切换到主线程上。 ***

问题: 以下代码有什么问题?如何修复?

@interface TTWaitController : UIViewController

@property (strong, nonatomic) UILabel *alert;

@end

@implementation TTWaitController

- (void)viewDidLoad
{
    CGRect frame = CGRectMake(20, 200, 200, 20);
    self.alert = [[UILabel alloc] initWithFrame:frame];
    self.alert.text = @"Please wait 10 seconds...";
    self.alert.textColor = [UIColor whiteColor];
    [self.view addSubview:self.alert];

    NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];
    [waitQueue addOperationWithBlock:^{
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
        self.alert.text = @"Thanks!";
    }];
}

@end

@implementation TTAppDelegate

- (BOOL)application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = [[TTWaitController alloc] init];
    [self.window makeKeyAndVisible];
    return YES;
}

这段代码是想提醒用户等待10s,10s后在标签上显示“Thanks”,但多线程代码部分NSOperationQueue的addOperationWithBlock函数不能保证block里面的语句是在主线程中运行的,UILabel显示文字属于UI更新,必须要在主线程进行,否则会有未知的操作,无法在界面上及时正常显示。

解决方法是将UI更新的代码写在主线程上即可,代码同步到主线程上主要有三种方法:NSThread、NSOperationQueue和GCD,三个层次的多线程都可以获取主线程并同步。

NSThread级主线程同步:

NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];
[waitQueue addOperationWithBlock:^{
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
    // 同步到主线程     [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
}];

/** * UI更新函数 */
- (void)updateUI {
    self.alert.text = @"Thanks!";
}

NSOperationQueue级主线程同步:

NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];
[waitQueue addOperationWithBlock:^{
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
    // 同步到主线程     [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        self.alert.text = @"Thanks!";
    }];
}];

GCD级主线程同步:

NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];
[waitQueue addOperationWithBlock:^{
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
    // 同步到主线程     dispatch_async(dispatch_get_main_queue(), ^{
        self.alert.text = @"Thanks!";
    });
}];

作者:爱笑的猫mi
链接:https://www.jianshu.com/p/80848fdd1e2a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上一篇下一篇

猜你喜欢

热点阅读