ios线程形式有三种。 ios4后,苹果改用GCD多线程。线程可以用来干什么呢?多线程可以用来后台加载图片更新主线程UI,或者请求网络数据等等。

Thread

使用 NSThread 来创建线程有两种方法:

1
2
3
4
5
6
7
8
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

NSThread* myThread = [[NSThread alloc] initWithTarget:self

selector:@selector(myThreadMainMethod:)

object:nil];
[myThread start]; // Actually create the thread

如果你拥有一个 NSThread 对象,它的线程当前正运行,你可以给该线程发送 消息的唯一方法是在你应用程序里面的任何对象使用 performSelector:onThread:withObject:waitUntilDone:方法。它是实现线程间通 信的便捷方法.

线程通信:
1.在指定线程上执行操作
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];

2.在主线程上执行操作

[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];

3.在当前线程执行操作

[self performSelector:@selector(run) withObject:nil];

NSOperation

通常 NSOperation 会和NSOperationQueue 结合起来使用。 在之前,我们应该了解一下NSOperation .
Cocoa operations是基于 Obective-C实现的,类 NSOperation 以面向对象的方式封装了用户需要执行的操作,我们只要聚焦于我们需要做的事情,而不必太操心线程的管理,同步等事情,
因为NSOperation已经为我们封装了这些事情。 NSOperation 是一个抽象基类,我们必须使用它的子类。iOS 提供了两种默认实现:NSInvocationOperation 和 NSBlockOperation。
代码示例:

1
2
3
4
5
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];  //主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //自定义队列
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//任务执行 }];
[queue addOperation:operation];

如果我们想在一个NSOperation执行完毕后做一些事情,就调用NSOperation的setCompletionBlock方法来设置想做的事情
operation.completionBlock = ^() {
NSLog(@”执行完毕”);
};

NSInvocationOperation

基于一个对象和selector来创建操作。如果你已经有现有的方法来执行需要的任务,就可以使用这个类
// 这个操作是:调用self的run方法
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 开始执行任务(同步执行)
[operation start];

GCD

单列模式

单例就是全局都只有一个对象存在,而且是在整个App运行过程中都存在。 做用户数据存储时,通常都会用单例存储,因为应用在所有操作中,经常要求先登录.

1
2
3
4
5
6
7
8
9
10
11
+ (instancetype)shared {
static HYBUserManager *sg_userManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (sg_userManager == nil) {
sg_userManager = [[HYBUserManager alloc] init];
}
});

return sg_userManager;
}

相信大家对dispatch_once都不陌生了,我将和大家一起探究dispatch_once的更多细节。
dispatch_once的作用正如其名:对于某个任务执行一次,且只执行一次。 dispatch_once函数有两个参数,第一个参数predicate用来保证执行一次,第二个参数是要执行一次的任务block。
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
// some one-time task
});
dispatch_once被广泛使用在单例、缓存等代码中,用以保证在初始化时执行一次某任务。
dispatch_once在单线程程序中毫无意义,但在多线程程序中,其低负载、高可依赖性、接口简单等特性,赢得了广大消费者的一致五星好评。

dispatch_once使得block中的代码执行且只执行一次,在多线程竞态时,使其他线程进入等待状态直至block执行完毕,并且还保证无竞态时执行效率与非线程安全的if语句效率相当。
dispatch_once内部使用了大量的原子操作来替代锁与信号量,这使得其效率大大提升,但带来的是维护和阅读性的降低。
dispatch_once被大量使用在构建单例上,apple也推荐如此。

Dispatch Group

我们可以使用dispatch_group_async函数将多个任务关联到一个Dispatch Group和相应的queue中,group会并发地同时执行这些任务。而且Dispatch Group可以用来阻塞一个线程, 直到group关联的所有的任务完成执行。有时候你必须等待任务完成的结果,然后才能继续后面的处理。
下面用Dispatch Group优化上面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

1. // 根据url获取UIImage
2. - (UIImage *)imageWithURLString:(NSString *)urlString {
3. NSURL *url = [NSURL URLWithString:urlString];
4. NSData *data = [NSData dataWithContentsOfURL:url];
5. // 这里并没有自动释放UIImage对象
6. return [[UIImage alloc] initWithData:data];
7. }
8.
9. - (void)downloadImages {
10. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
11.
12. // 异步下载图片
13. dispatch_async(queue, ^{
14. // 创建一个组
15. dispatch_group_t group = dispatch_group_create();
16.
17. __block UIImage *image1 = nil;
18. __block UIImage *image2 = nil;
19.
20. // 关联一个任务到group
21. dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
22. // 下载第一张图片
23. NSString *url1 = @"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";
24. image1 = [self imageWithURLString:url1];
25. });
26.
27. // 关联一个任务到group
28. dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
29. // 下载第一张图片
30. NSString *url2 = @"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";
31. image2 = [self imageWithURLString:url2];
32. });
33.
34. // 等待组中的任务执行完毕,回到主线程执行block回调
35. dispatch_group_notify(group, dispatch_get_main_queue(), ^{
36. self.imageView1.image = image1;
37. self.imageView2.image = image2;
38.
39. // 千万不要在异步线程中自动释放UIImage,因为当异步线程结束,异步线程的自动释放池也会被销毁,那么UIImage也会被销毁
40.
41. // 在这里释放图片资源
42. [image1 release];
43. [image2 release];
44. });
45.
46. // 释放group
47. dispatch_release(group);
48. });
49. }

dispatch_group_notify函数用来指定一个额外的block,该block将在group中所有任务完成后执行.

异步下载图片

1> GCD提供一个特殊的dispatch queue,可以在应用的主线程中执行任务。只要应用主线程设置了run loop(由CFRunLoopRef类型或NSRunLoop对象管理),就会自动创建这个queue,并且最后会自动销毁。非Cocoa应用如果不显式地设置run loop, 就必须显式地调用dispatch_main函数来显式地激活这个dispatch queue,否则虽然你可以添加任务到queue,但任务永远不会被执行。
2> 调用dispatch_get_main_queue函数获得应用主线程的dispatch queue,添加到这个queue的任务由主线程串行化执行
3> 代码实现,比如异步下载图片后,回到主线程显示图片

1
2
3
4
5
6
7
8
9
10
1. // 异步下载图片  
2. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3. NSURL *url = [NSURL URLWithString:@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg"];
4. UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
5.
6. // 回到主线程显示图片
7. dispatch_async(dispatch_get_main_queue(), ^{
8. self.imageView.image = image;
9. });
10. });

评论