本文转载自的博文:http://www.cnblogs.com/huluo666/p/3645889.html,修改了部分代码和贴图,如有侵犯版权请与我联系删除。
多线程这个概念的接触是蛮早的时候了,当时还是单核单CPU的时候,Thread这个概念已经出现了,当时比较流行的方案是时间片轮流,线程可以优先级抢占,但一次只能运行一个线程,实际上多线程是不能真正并行处理的,只是宏观上表现的多线程在齐头并进。现在硬件进步了很多,多核的CPU时代来临了,于是线程开始了真正意义上的并行处理,多线程也作为越来越重要的一个部分需要掌握。
iOS中关于线程的创建和运行,提供了3种方法:NSThread,NSOperation和GCD。这三种方式抽象程度越来越高,所以编写代码是越来越简单的。
我们先来看NSThread吧。
NSThread比其他两个都要更轻量级,但需要自己来管理线程的生命周期和同步,代码的编写上比其他两个复杂。对于线程的技术,苹果的官方文档上给出了一张表:
Technology | Description |
---|---|
Cocoa threads | Cocoa implements threads using the |
POSIX threads | POSIX threads provide a C-based interface for creating threads. If you are not writing a Cocoa application, this is the best choice for creating threads. The POSIX interface is relatively simple to use and offers ample flexibility for configuring your threads. For more information, see |
Multiprocessing Services | Multiprocessing Services is a legacy C-based interface used by applications transitioning from older versions of Mac OS. This technology is available in OS X only and should be avoided for any new development. Instead, you should use the
|
苹果虽然支持3种技术,但我们实际中最常用的也就是第一种,使用NSThread来进行线程的控制。NSThread有2种初始化方式,一种是传统的init方式(initWithTarget:selector:object:),另一种是类方法(+ detachNewThreadSelector:toTarget:withObject:)
initWithTarget方法用法如下:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage:) object:IMAGE_URL];[thread start];
这个里面的@selector就是线程的入口点,只能接受一个传入参数,并且没有返回值。还有一个需要强调一下,init仅仅是创建了线程,要线程运行还需要调用start方法。
如果要换成detachNewThreadSelector的类方法,用法如下:
[NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:IMAGE_URL];
我们可以看到参数很像,但线程会立即运行,不需要手动调用start方法。
我们还是看代码吧,这个代码很简单,是实现后台下载,并在一张UIImage上显示。
用NSThread的方法,采用initWithTarget的做法:
#import "ViewController.h"#define IMAGE_URL @"http://williamzhang-public.qiniudn.com/DSC_0069.jpg"@interface ViewController ()@end@implementation ViewController@synthesize imageView;- (void)downloadImage:(NSString*)url{ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]]; UIImage *image = [[UIImage alloc] initWithData:data]; if (image) { [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES]; } [pool drain];}- (void)updateUI:(UIImage*)image{ self.imageView.image = image;}- (void)viewDidLoad{ [super viewDidLoad]; NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage:) object:IMAGE_URL]; [thread start]; }- (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}- (void)dealloc { [imageView release]; [super dealloc];}@end
在viewDidLoad中,我们生成一个线程并运行,线程的入口点就是downloadImage:方法,用来下载图片,并通知主线程刷新界面。
如果采用detachNewThreadSelector方法就是把viewDidLoad方法稍微改改就行:- (void)viewDidLoad{ [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:IMAGE_URL];}
我们在开发中始终需要记住main thread是不能运行长时间没有响应的任务的,上面的例子就是一个很典型的多线程应用,但是苹果还是提供了一种更近简单的方式帮助我们简化开发的麻烦。这就是NSObject类提供的performSelectorInBackground:withObject:方法和performSelectorOnMainThread:withObject:waitUntilDone:方法。在上面的例子中已经使用了performSelectorOnMainThread:withObject:waitUntilDone:方法,在下载图片后就是用这个方法让主线程刷新UI的。
- (void)viewDidLoad{ [super viewDidLoad]; [self performSelectorInBackground:@selector(downloadImage:) withObject:IMAGE_URL];}
如果这样写的话,虽然也是本质还是多线程,但你根本感觉不到NSThread的存在,可以简单的理解就运行一个后台的任务,在不少场合还是更简单清晰一些。
最后要说的一个是生成的线程,对内存的管理也是要自己负责的,所以需要生成autorelease pool,如果你开启了ARC功能,那么可以简单的用@autoreleasepool这个关键字。