多线程、GCD
多线程、GCD
这篇笔记大部分摘录自:伯恩的遗产的 《关于iOS多线程,你看我就够了》。他的总结足够好了,同时我看《iOS与OS X多线程和内存管理》补充了一些GCD方面的方法,只是作为笔记以后方便查阅,所以文笔很烂, 伯恩的遗产 的博客文笔幽默,总结也很多,建议去收录查看。
在iOS中有4套多线程的方案:
- Pthreads
- NSThread
- GCD
- NSOperation & NSOperationQueue
Pthreads
类Unix操作系统中的POSIX线程API,基于标准C语言。
#import <pthread.h>
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
pthread_t thread;
//创建一个线程并自动执行
pthread_create(thread, NULL, start, NULL);
}
void *start(void *data) {
NSLog(@"%@", [NSThread currentThread]);
return NULL;
}
使用Pthreads,需要手动处理线程的各个状态的转换即管理生命周期,比如,这段代码虽然创建了一个线程,但并没有销毁。
NSThread
封装成面向对象,但是生命周期仍需要手动管理。
- 先创建线程类,再启动
// 创建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];
// 启动
[thread start];
- 创建并自动启动
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
- 使用 NSObject 的方法创建并自动启动
[self performSelectorInBackground:@selector(run:) withObject:nil];
- 其他方法
//取消线程
-(void)cancel;
//启动线程
-(void)start;
//判断某个线程的状态的属性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
//设置和获取线程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
//获取当前线程信息
+(NSThread *)currentThread;
//获取主线程信息
+(NSThread *)mainThread;
//使当前线程暂停一段时间,或者暂停到某个时刻
+(void)sleepForTimeInterval:(NSTimeInterval)time;
+(void)sleepUntilDate:(NSDate *)date;
GCD
Dispatch Queue
将要执行的任务追加到适当的Dispatch Queue中即可。
dispatch_async(queue,^{
//想要执行的代码
});
- dispatch_queue_create
创建自己的线程队列,其中线程队列种类分为两种,DISPATCH_QUEUE_SERIAL和DISPATCH_QUEUE_CONCURRENT,DISPATCH_QUEUE_SERIAL类型的队列代表加入队列的任务是一个一个的执行的,叫做串行队列。而DISPATCH_QUEUE_CONCURRENT则代表不会等待现在执行的任务结束就会执行下一个任务,叫做并行队列。
//串行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
- dispatch_release、dispatch_retain
通过dispatch_queue_create创建的队列必须由程序员自己释放,手动创建的队列使用引用计数式内存管理,需要手动管理。
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue,^{NSLog(@"%@", [NSThread currentThread]);});
dispatch_release(queue);
- dispatch_get_main_queue()
获取系统主队列。是一个特殊的DISPATCH_QUEUE_SERIAL串行队列,主要进行刷新UI等操作。dispatch_release、dispatch_retain等方法对主队列是无效的。
dispatch_queue_t main_queue = dispatch_get_main_queue();
- dispatch_get_global_queue
获取全局并行队列。只要是并行任务一般都加入到这个队列,这是系统提供的一个并发队列,具有4种优先级。
//高优先级
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//默认优先级
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//低优先级
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//后台优先级
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
- dispatch_set_target_queue
通过dispatch_queue_create创建的队列都是默认优先级,要变更优先级则需要使用dispatch_set_target_queue函数,将第二个参数的队列的优先级赋值给第一个参数的队列。
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
//高优先级
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(queue,global_queue);
Dispatch Method
- dispatch_sync
同步任务,会阻塞当前线程。在使用dispatch_sync同步方法时,尤其要注意类型为DISPATCH_QUEUE_SERIAL的队列,需要非常小心,很容易造成死锁现象。尤其是dispatch_get_main_queue主队列,会直接导致界面的无响应。
NSLog(@"之前 - %@", NSThread.currentThread());
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync - %@", NSThread.currentThread());
});
NSLog(@"之后 - %@", NSThread.currentThread());
比如上例中,只会打印第一句:之前 - < NSThread: 0x7fb3a9e16470 >{number = 1, name = main} ,然后主线程就卡死了。
- dispatch_async
异步任务,不会阻塞当前线程。
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
那么以下的代码会产生什么结果呢?
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"之前 - %@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"sync之前 - %@", [NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"sync - %@", [NSThread currentThread]);
});
NSLog(@"sync之后 - %@", [NSThread currentThread]);
});
NSLog(@"之后 - %@", [NSThread currentThread]);
答案:
2015-07-30 02:06:51.058 test[33329:8793087] 之前 – < NSThread: 0x7fe32050dbb0 >{number = 1, name = main}
2015-07-30 02:06:51.059 test[33329:8793356] sync之前 – < NSThread: 0x7fe32062e9f0 >{number = 2, name = (null)}
2015-07-30 02:06:51.059 test[33329:8793087] 之后 – < NSThread: 0x7fe32050dbb0 >{number = 1, name = main}
很明显 sync - %@ 和 sync之后 - %@ 没有被打印出来!这是为什么呢?我们再来按执行顺序一步步分析一下:
- 使用 DISPATCH_QUEUE_SERIAL 这个参数,创建了一个 串行队列。
- 打印出 之前 - %@ 这句。
- dispatch_async 异步执行,所以当前线程不会被阻塞,于是有了两条线程,一条当前线程继续往下打印出 之后 - %@这句, 另一台执行 Block 中的内容打印 sync之前 - %@这句。因为这两条是并行的,所以打印的先后顺序无所谓。
- dispatch_sync同步执行,于是它所在的线程会被阻塞,一直等到 sync 里的任务执行完才会继续往下。于是 sync 就高兴的把自己 Block 中的任务放到 queue 中,可谁想 queue 是一个串行队列,一次执行一个任务,所以 sync 的 Block 必须等到前一个任务执行完毕,可万万没想到的是 queue 正在执行的任务就是被 sync 阻塞了的那个。于是又发生了死锁。所以 sync 所在的线程被卡死了。剩下的两句代码自然不会打印。
- dispatch_after
在指定时间后执行任务。在指定时间之后,用dispatch_async函数追加block到指定queue。注意这个指定时间,是指最快时间,实际时间可能有所延迟。第一个参数是指定的dispatch_time_t时间类型,第二个参数指定的毫秒数。
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, queue, ^{
NSLog(@"三秒后打印");
});
DISPATCH_TIME_NOW返回当前时间的dispatch_time_t类型。NSEC_PER_SEC是秒的单位,NSEC_PER_MSEC是毫秒的单位。
dispatch_time用于计算相对时间,dispatch_walltime用于计算绝对时间,比如指定某年某月某日的时间。
dispatch_time_t类型可以通过NSDate类对象来生成,用以下的方法.
dispatch_time_t getDispatchTimeByDate(NSDate *date){
NSTimeInterval interval;
double second, subsecond;
struct timespec time;
dispatch_time_t milestone;
interval = [date timeIntervalSince1970];
subsecond = modef(interval, &second);
time.tv_sec = second;
time.tv_nsec = subsecond * NSEC_PER_SEC;
milestone = dispatch_walltime(&time, 0);
return milestone;
}
- dispatch_barrier_async
这个方法主要用于阻塞DISPATCH_QUEUE_CONCURRENT并行队列(注意是阻塞 queue ,而不是阻塞当前线程),这个方法会等待排在队列前面的任务执行完后才会开始执行自己,自己执行完毕后,再会取消阻塞,使这个 queue 中排在它后面的任务继续执行。如果你传入的是其他类型的 queue, 那么它就和 dispatch_async 一样了。
主要用于在类似场景,读取处理的时候可以并行处理,提高读取效率,但是写入的时候,要在没有读取的情况下执行,并且写入处理结束前,不能有其他读取情况。
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
//并行读取操作
dispatch_async(queue, blk0_for_reading);
dispatch_async(queue, blk1_for_reading);
dispatch_async(queue, blk2_for_reading);
dispatch_async(queue, blk3_for_reading);
//在前面0,1,2,3个block都执行结束后才会执行写入的block
dispatch_barrier_async(queue, blk_for_writing);
//在前面的写入block执行结束后,才会继续并行读取
dispatch_async(queue, blk4_for_reading);
dispatch_async(queue, blk5_for_reading);
dispatch_async(queue, blk6_for_reading);
dispatch_release(queue);
- dispatch_barrier_sync
使用和dispatch_barrier_async类似,传入 自定义的并发队列(DISPATCH_QUEUE_CONCURRENT),它和上一个方法一样的阻塞 queue,不同的是 这个方法还会 阻塞当前线程。如果你传入的是其他的 queue, 那么它就和 dispatch_sync 一样了。
- dispatch_apply
按照指定的次数将指定的block追加到dispatch_queue_t队列中(一般是指DISPATCH_QUEUE_CONCURRENT队列,如果是DISPATCH_QUEUE_SERIAL队列就和执行相当次数的dispatch_sync效果一样),注意dispatch_apply会阻塞当前线程,并等待全部处理结束后才会执行下一步。第一个参数是重复次数,第二个参数是追加的queue,第三个参数是带有处理次数的block。
主要用于类似数组处理这样的并行处理,由于和dispatch_sync一样会阻塞线程,因此建议在dispatch_async函数中非同步的执行dispatch_apply函数。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
dispatch_apply([array count], queue, ^(size_t index){
//并行处理包含在array中的所有对象
NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
});
//dispatch_apply处理完数组后进行的处理
NSLog(@"Done After Array");
//在主队列中非同步执行
dispatch_async(dispatch_get_main_queue(), ^{
//更新界面
NSLog(@"Done");
});
});
- dispatch_suspend、dispatch_resume
挂起队列函数和恢复队列函数。这两个函数对已经执行的处理没有影响。挂起后,追加到queue中的尚未执行的处理在此之后停止执行。而恢复则使得这些处理能够继续执行。
dispatch_suspend(queue);
dispatch_resume(queue);
- dispatch_once
保证block即使在多线程环境下也只执行一次。一般用于生成单例
static dispatch_once_t pred;
dispatch_once(&pred, ^{
//初始化
});
Dispatch Group
dispatch_group_t即是队列组,队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。
- dispatch_group_create、dispatch_group_async、dispatch_group_notify
创建队列组。和dispatch_queue_create一样,也需要手动管理释放,一样使用dispatch_release、dispatch_retain来进行内存管理。
dispatch_group_async追加block到指定的队列组和队列。第一个参数是所属的dispatch_group_t队列组,第二个参数是指定的dispatch_queue_t队列,第三个参数是要执行的block。
dispatch_group_notify函数的作用是在dispatch_group_t队列组中所有的任务都执行结束后,通知dispatch_queue_t队列执行结束处理的block。
//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//3.添加执行任务
dispatch_group_async(group, queue, ^{
NSLog(@"group-01 - %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"group-02 - %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"group-03 - %@", [NSThread currentThread]);
});
//4.都完成后会自动通知主队列
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
dispatch_release(group);
- dispatch_group_wait
dispatch_group_wait的作用仅仅是等待队列组中全部处理执行结束。第一个参数的监视的dispatch_group_t队列组,第二个参数是指定等待的dispatch_time_t时间,使用DISPATCH_TIME_FOREVER,则代表一直等待,使用DISPATCH_TIME_NOW,则是当前时间。
dispatch_group_wait函数返回值如果不为0,则代表dispatch_group_t队列组仍然在执行任务,为0则是执行结束。因此第二个参数如果是DISPATCH_TIME_FOREVER,则代表返回为0,如果是DISPATCH_TIME_NOW,则是判断当前的dispatch_group_t队列组是否仍然在执行任务。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//3.添加执行任务
dispatch_group_async(group, queue, ^{
NSLog(@"group-01 - %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"group-02 - %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"group-03 - %@", [NSThread currentThread]);
});
//4.等待一定时间,检查是否任务处理结束
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if(result == 0){
//代表所有任务执行完毕
}else{
//代表还有任务在执行中
}
dispatch_release(group);
Dispatch Semaphore
在并行执行处理更新数据时,会产生数据不一致的情况。虽然可以使用DISPATCH_QUEUE_SERIAL串行队列和dispatch_barrier_async可以避免这类问题,但是有必要进行更细粒度的排他控制,就需要使用到Dispatch Semaphore。
比如下例就是通过Dispatch Semaphore的实现保证多线程添加数组的安全性。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//设定同时访问NSMutableArray类对象的线程同时只能有1个
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray *array = [[NSMutableArray alloc] init];
for(int i = 0; i < 100000; ++ i){
dispatch_async(queue,^{
//一直等待,直到Dispatch Semaphore的计数值达到大于等于1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//由于Dispatch Semaphore的计数值大于等于1,dispatch_semaphore_wait会等到计数器为0的时候,才会执行下一步
[array addObject:[NSNumber numberWithInt:i]];
//将Dispatch Semaphore的计数值加1.
dispatch_semaphore_signal(semaphore);
});
}
//释放掉semaphore
dispatch_release(semaphore);
Dispatch I/O
当读取较大文件时,将文件分成合适大小并使用并行队列进行读取,读取速度要快很多。分割读取数据可以通过Dispatch Data来进行简单的结合和分割。通过Dispatch I/O能对文件进行并发读写。
pipe_q = dispatch_queue_create("PipeQ",NULL);
pipe_channel = dispatch_io_create(DISPATCH_STREAM, fd, pipe_q, ^{
close(fd);
});
*out_fd = fdpair[1];
dispatch_io_set_low_water(pipe_channel, SIZE_MAX);
dispatch_io_read(pip_channel, 0 ,SIZE_MAX, pipe_q,^(bool done, dispatch_data_t pipedata, int err){
if (err == 0){
size_t len = dispatch_data_get_size(pipedata);
if(len > 0){
const char *bytes = NULL;
char *encoded;
dispatch_data_t md =dispatch_data_create_map(pipedata, (const void **)&bytes, &len);
encoded = asl_core_encode_buffer(bytes, len);
free(encoded);
_asl_send_message(NULL, merged_msg, -1, NULL);
als_msg_release(merged_msg);
dispatch_release(md);
}
}
if(done){
dispatch_semaphore_signal(sem)
dispatch_release(pipe_channel);
dispatch_release(pipe_q);
}
});
以上是摘自Log Api的源代码(Libc-763.11 gen.asl.c)。dispatch_io_create函数生成Dispatch I/O,并指定发生错误时执行的Block,以及执行该block的queue。dispatch_io_set_low_water设置一次读取的大小(分割大小),dispatch_io_read函数使用queue开始并行读取。每当各个分割文件块读取结束后,将含有文件块数据的Dispatch Data传递给dispatch_io_read函数指定的读取结束时的回调block。回调用的block分析传递的Dispatch Data并进行结合处理。
NSOperation、NSOperationQueue
NSOperation 是苹果公司对 GCD 的封装,完全面向对象,所以使用起来更好理解。 大家可以看到 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列 。操作步骤也很好理解:
- 将要执行的任务封装到一个 NSOperation 对象中。
- 将此任务添加到一个 NSOperationQueue 对象中。
- NSOperation
NSOperation 只是一个抽象类,不能直接使用。NSInvocationOperation 和 NSBlockOperation是封装任务的两个子类。创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。
- NSInvocationOperation
//1.创建NSInvocationOperation对象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//2.开始执行
[operation start];
- NSBlockOperation
//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//2.开始任务
[operation start];
其中:addExecutionBlock:,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务会并发执行。但是它会它会在主线程和其它的多个线程执行这些任务。
- NSOperationQueue
NSOperation对象的start()方法来启动这个任务,但是这样做他们默认是同步执行的。就算是addExecutionBlock方法,也会在当前线程和其他线程中执行,也就是说还是会占用当前线程。这是就要用到队列 NSOperationQueue 了。而且,按类型来说的话一共有两种类型:主队列、其他队列。只要添加到队列,会自动调用任务的start()方法。
- 获取主队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
- 其他队列
//1.创建一个其他队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//4.队列添加任务
[queue addOperation:operation];
NSOperationQueue有一个参数maxConcurrentOperationCount最大并发数,用来设置最多可以让多少个任务同时执行。当你把它设置为1的时候,就是串行队列。
- -(void)addOperationWithBlock:(void (^)(void))block;
//1.任务一:下载图片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//2.任务二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//3.任务三:上传图片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上传图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//4.设置依赖
[operation2 addDependency:operation1]; //任务二依赖任务一
[operation3 addDependency:operation2]; //任务三依赖任务二
//5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
注意:
- 不能添加相互依赖,会死锁,比如 A依赖B,B依赖A。
- 可以使用 removeDependency 来解除依赖关系。
- 可以在不同的队列之间依赖,反正就是这个依赖是添加到任务身上的,和队列没关系。
- 其他方法
- NSOperation
BOOL executing; //判断任务是否正在执行
BOOL finished; //判断任务是否完成
void (^completionBlock)(void); //用来设置完成后需要执行的操作
-(void)cancel; //取消任务
-(void)waitUntilFinished; //阻塞当前线程直到此任务执行完毕- NSOperationQueue
NSUInteger operationCount; //获取队列的任务数
-(void)cancelAllOperations; //取消队列中所有的任务
-(void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的所有任务执行完毕
[queue setSuspended:YES]; // 暂停queue
[queue setSuspended:NO]; // 继续queue
线程同步
- 互斥锁
给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。
@synchronized(self) {
//需要执行的代码块
}
- 同步执行
//GCD
//需要一个全局变量queue,要让所有线程的这个操作都加到一个queue中
dispatch_sync(queue, ^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval:0.1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
});
//NSOperation & NSOperationQueue
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval:1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
}];
[queue addOperation:operation];
[operation waitUntilFinished];
//后续要做的事
延迟执行
- NSObject
// 3秒后自动调用self的run:方法,并且传递参数:@"abc"
[self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];
- GCD
// 创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 设置延时,单位秒
double delay = 3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{
// 3秒后需要执行的任务
});
- NSTimer
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];
单例模式
@interface Tool : NSObject <NSCopying>
+ (instancetype)sharedTool;
@end
@implementation Tool
static id _instance;
+ (instancetype)sharedTool {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[Tool alloc] init];
});
return _instance;
}
@end
从其他线程回到主线程的方法
- NSThread
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
- GCD
dispatch_async(dispatch_get_main_queue(), ^{
//返回主线程
});
- NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//返回主线程
}];

浙公网安备 33010602011771号