多线程、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之后 - %@ 没有被打印出来!这是为什么呢?我们再来按执行顺序一步步分析一下:

  1. 使用 DISPATCH_QUEUE_SERIAL 这个参数,创建了一个 串行队列。
  2. 打印出 之前 - %@ 这句。
  3. dispatch_async 异步执行,所以当前线程不会被阻塞,于是有了两条线程,一条当前线程继续往下打印出 之后 - %@这句, 另一台执行 Block 中的内容打印 sync之前 - %@这句。因为这两条是并行的,所以打印的先后顺序无所谓。
  4. 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 的 任务 和 队列 。操作步骤也很好理解:

  1. 将要执行的任务封装到一个 NSOperation 对象中。
  2. 将此任务添加到一个 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 &lt; 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 &lt; 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];

注意:

  1. 不能添加相互依赖,会死锁,比如 A依赖B,B依赖A。
  2. 可以使用 removeDependency 来解除依赖关系。
  3. 可以在不同的队列之间依赖,反正就是这个依赖是添加到任务身上的,和队列没关系。
  • 其他方法
    • 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:^{
	//返回主线程
}];
posted @ 2015-08-14 18:20  前尘如梦  阅读(240)  评论(0)    收藏  举报