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)argwaitUntilDone:(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; }
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- js判断客户端是iOS还是Android等移动终端的方法
- IOS开发环境windows化攻略
- .net平台推送ios消息的实现方法
- 探讨Android与iOS,我们将何去何从?
- Android、iOS和Windows Phone中的推送技术详解
- IOS 改变键盘颜色代码
- Android和IOS的浏览器中检测是否安装某个客户端的方法
- javascript实现阻止iOS APP中的链接打开Safari浏览器
- iOS开发之路--微博OAuth授权_取得用户授权的accessToken
- ios通过按钮点击异步加载图片
- ios中图像进行压缩方法汇总
- IOS检测指定路径的文件是否存在
- iOS、Mac OS X系统中编程实现汉字转拼音的方法(超级简单)