您的位置:首页 > 移动开发 > IOS开发

iOS开发 - 多线程间通信

2015-06-04 19:52 330 查看

一、线程间通信的意义

       一个iOS程序运行时对应一个进程, 该进程至少包含一个主线程, 实际的程序通常是多线程运行的, 而多个线程之间常常涉及到通信问题, 有时候需要开启一个子线程来完成一些耗时操作, 但是子线程执行完后又需要回到主线程更新UI界面, 相当于子线程执行完后, 通知主线程更新UI界面。

二、阻塞主线程的情况

      主线程是顺序执行的, 如果耗时操作放在z主线程执行, 势必影响主线程执行后面的其他操作。
      测试1: 耗时操作全部放在主线程执行, 会阻塞主线程的执行
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *bigImageView;
@property (weak, nonatomic) IBOutlet UIImageView *smallImageView;
@end

@implementation ViewController

- (void)viewDidLoad
{
[self downloadAndSetupIconImageView];
}

- (void)downloadAndSetupIconImageView
{
NSString *urlStr = @"http://localhost:8080/iOSServer/picture/pic01.png";
NSURL *url = [NSURL URLWithString:urlStr];
// 线程的编号为1, 所以是在主线程执行
NSLog(@"%@", [NSThread currentThread]);
// 假定要下载4000张图片
for(int i = 0; i< 4000; i++)
{
NSData *data = [NSData dataWithContentsOfURL:url];
}
self.bigImageView.image = [UIImage imageWithData:data];
self.smallImageView.image = [UIImage imageNamed:@"009.png"];
}
@end
<span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">        </span><span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">效果: 程序顺序执行, 等待几秒钟后bigImageView和smallImageView开始显示;</span>

三、创建子线程防止阻塞主线程  

       有没有方法可以让图片的下载不影响smallImageView的显示呢?

       可以将图片的下载放在子线程里面去执行。

<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">- (void)downloadAndSetupIconImageView</span>
{
NSString *urlStr = @"http://localhost:8080/iOSServer/picture/pic01.png";
NSURL *url = [NSURL URLWithString:urlStr];
__block  NSData *data = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 线程的编号=2, 在子线程执行
NSLog(@"%@", [NSThread currentThread]);
for (int i = 0; i< 2000; i++) {
NSData *data = [NSData dataWithContentsOfURL:url];
}
});
self.bigImageView.image = [UIImage imageWithData:data];
self.smallImageView.image = [UIImage imageNamed:@"009.png"];
}

    
    分析: 程序是顺序执行, 在开启子线程后, 程序会顺序往下执行, 此时bigImageView不显示(data = nil),
             但是smallImageView立即显示。也就是说, 阻塞的问题得到了解决, 但是出现了新的问题。
              新手学习多线程可能会犯以上低级错误。

四、线程间通信的方式

       子线程执行完后, 应该通知主线程更新UI, 这就是线程间通信

       方式1: 直接在GCD内部再次调用主线程执行操作

- (void)downloadAndSetupIconImageView
{
NSString *urlStr = @"http://localhost:8080/iOSServer/picture/pic01.png";
NSURL *url = [NSURL URLWithString:urlStr];
// 下载图片, 耗时操作
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = nil;
for (int i = 0; i< 2000; i++) {
data = [NSData dataWithContentsOfURL:url];
}
dispatch_async(dispatch_get_main_queue(), ^{
self.bigImageView.image = [UIImage imageWithData:data];
});
});
self.smallImageView.image = [UIImage imageNamed:@"009.png"];
}

       效果: 程序启动后, 立即显示smallImageView, 等待数秒后, 显示bigImageView


      

 
   

   方式2: 调用NSThread类的方法

   - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg
waitUntilDone:(BOOL)wait
NS_AVAILABLE(10_5, 2_0);
   - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

- (void)downloadAndSetupIconImageView
{

NSString *urlStr = @"http://localhost:8080/iOSServer/picture/pic01.png";
NSURL *url = [NSURL URLWithString:urlStr];

// 创建子线程下载图片, 耗时操作
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = nil;
for (int i = 0; i< 2000; i++) {
data = [NSData dataWithContentsOfURL:url];
}
UIImage *image = [UIImage imageWithData:data];

[self performSelector:@selector(setBigImageView:)
onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];

});
self.smallImageView.image = [UIImage imageNamed:@"009"];
}

- (void)setBigImageView:(UIImage *)image
{
self.bigImageView1.image = image;
}


五、细节

       细节1: 因为NSOperationQueue基于GCD, 所以GCD的线程间通信方式也适用于NSOperationQueue
- (void)downloadAndSetupIconImageView
{
NSString *urlStr = @"http://localhost:8080/iOSServer/picture/pic01.png";
NSURL *url = [NSURL URLWithString:urlStr];

// 子线程
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSData *data = nil;
for (int i = 0; i< 2000; i++) {
data = [NSData dataWithContentsOfURL:url];
}
// 主线程
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
self.bigImageView1.image  = [UIImage imageWithData:data];
}];
}];

self.smallImageView.image = [UIImage imageNamed:@"009"];
}

    细节2: 以下方法也可以开启子线程执行操作
    - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)argNS_AVAILABLE(10_5,
2_0);
    范例:
- (void)downloadAndSetupIconImageView
{
[self performSelectorInBackground:@selector(download) withObject:nil];
self.smallImageView.image = [UIImage imageNamed:@"009"];
}

/*
开启子线程进行图片批量下载
*/
- (void)download
{
NSLog(@"dowload - %@", [NSThread currentThread]);
NSString *urlStr = @"http://localhost:8080/iOSServer/picture/pic01.png";
NSURL *url = [NSURL URLWithString:urlStr];
NSData *data = nil;
for (int i = 0; i< 2000; i++) {
data = [NSData dataWithContentsOfURL:url];
}
UIImage *image = [UIImage imageWithData:data];
[self performSelector:@selector(setBigImageView:)
onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}

- (void)setBigImageView:(UIImage *)image
{
self.bigImageView1.image = image;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  iOS 多线程通信