1、简介:
1.1iOS有三种多线程编程的技术,分别是:
1.、NSThread
2、CocoaNSOperation(iOS多线程编程之NSOperation和NSOperationQueue的使用)
3、GCD全称:GrandCentralDispatch(iOS多线程编程之GrandCentralDispatch(GCD)介绍和使用)
这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。
这篇我们主要介绍和使用NSThread,后面会继续2、3的讲解和使用。
1.2三种方式的优缺点介绍:
NSThread:
优点:NSThread比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
NSThread实现的技术有下面三种:
Cocoathreads
POSIXthreads
MultiprocessingServices
一般使用cocoathread技术。
Cocoaoperation
优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoaoperation相 关的类是NSOperation,NSOperationQueue。NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用 它定义好的两个子类:NSInvocationOperation和NSBlockOperation。创建NSOperation子类的对象,把对 象添加到NSOperationQueue队列里执行。
GCD
GrandCentralDispatch(GCD) 是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如 NSThread,NSOperationQueue,NSInvocationOperation等技术的很高效和强大的技术。现在的iOS系统都 升级到6了,所以不用担心该技术不能使用。
介绍完这三种多线程编程方式,我们这篇先介绍NSThread的使用。
2、NSThread的使用
2.1NSThread有两种直接创建方式:
-(id)initWithTarget:(id)targetselector:(SEL)selectorobject:(id)argument
+(void)detachNewThreadSelector:(SEL)aSelectortoTarget:(id)aTargetwithObject:(id)anArgument
第一个是实例方法,第二个是类方法
[NSThreaddetachNewThreadSelector:@selector(doSomething:)toTarget:selfwithObject:nil];
NSThread*myThread=[[NSThreadalloc]initWithTarget:selfselector:@selector(doSomething:)object:nil];
[myThreadstart];
2.2参数的意义:
selector:线程执行的方法,这个selector只能有一个参数,而且不能有返回值。
target:selector消息发送的对象
argument:传输给target的唯一参数,也可以是nil
第一种方式会直接创建线程并且开始运行线程,第二种方式是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息
2.3PS:不显式创建线程的方法:
用NSObject的类方法performSelectorInBackground:withObject:创建一个线程:
[ObjperformSelectorInBackground:@selector(doSomething)withObject:nil];
2.4下载图片的例子:
2.4.1新建singeViewapp
新建项目,并在xib文件上放置一个imageView控件。按住control键拖到viewController.h文件中创建imageViewIBOutletViewController.m中实现:
#import"ViewController.h"
#definekURL@"/Mobile/UploadFiles_9667/201306/20130613152404754.jpg"
@interfaceViewController()
@end
@implementationViewController
d)downloadImage:(NSString*)url{
NSData*data=[[NSDataalloc]initWithContentsOfURL:[NSURLURLWithString:url]];
UIImage*image=[[UIImagealloc]initWithData:data];
if(image==nil){
}else{
[selfperformSelectorOnMainThread:@selector(updateUI:)withObject:imagewaitUntilDone:YES];
}
}
d)updateUI:(UIImage*)image{
self.imageView.image=image;
}
-(void)viewDidLoad
{
[superviewDidLoad];
NSThread*thread=[[NSThreadalloc]initWithTarget:selfselector:@selector(downloadImage:)object:kURL];
[threadstart];
}
-(void)didReceiveMemoryWarning
{
[superdidReceiveMemoryWarning];
}
@end
2.4.2线程间通讯
线程下载完图片后怎么通知主线程更新界面呢?
[selfperformSelectorOnMainThread:@selector(updateUI:)withObject:imagewaitUntilDone:YES];
performSelectorOnMainThread是NSObject的方法,除了可以更新主线程的数据外,还可以更新其他线程的比如:
用:performSelector:onThread:withObject:waitUntilDone:
运行下载图片:
图片下载下来了。
2.3线程同步
我们演示一个经典的卖票的例子来讲NSThread的线程同步:
.h
#import<UIKit/UIKit.h>
@classViewController;
@interfaceAppDelegate:UIResponder<UIApplicationDelegate>
{
inttickets;
intcount;
NSThread*ticketsThreadone;
NSThread*ticketsThreadtwo;
NSCondition*ticketsCondition;
NSLock*theLock;
}
@property(strong,nonatomic)UIWindow*window;
@property(strong,nonatomic)ViewController*viewController;
@end
-(BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
tickets=100;
count=0;
theLock=[[NSLockalloc]init];
ticketsCondition=[[NSConditionalloc]init];
ticketsThreadone=[[NSThreadalloc]initWithTarget:selfselector:@selector(run)object:nil];
[ticketsThreadonesetName:@"Thread-1"];
[ticketsThreadonestart];
ticketsThreadtwo=[[NSThreadalloc]initWithTarget:selfselector:@selector(run)object:nil];
[ticketsThreadtwosetName:@"Thread-2"];
[ticketsThreadtwostart];
self.window=[[UIWindowalloc]initWithFrame:[[UIScreenmainScreen]bounds]];
self.viewController=[[ViewControlleralloc]initWithNibName:@"ViewController"bundle:nil];
self.window.rootViewController=self.viewController;
[self.windowmakeKeyAndVisible];
returnYES;
}
-(void)run{
while(TRUE){
[theLocklock];
if(tickets>=0){
[NSThreadsleepForTimeInterval:0.09];
count=100-tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThreadcurrentThread]name]);
tickets--;
}else{
break;
}
[theLockunlock];
}
}
如果没有线程同步的lock,卖票数可能是-1.加上lock之后线程同步保证了数据的正确性。
上面例子我使用了两种锁,一种NSCondition,一种是:NSLock。NSCondition我已经注释了。
线程的顺序执行
他们都可以通过
[ticketsConditionsignal];发送信号的方式,在一个线程唤醒另外一个线程的等待。
比如:
#import"AppDelegate.h"
#import"ViewController.h"
@implementationAppDelegate
-(BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
tickets=100;
count=0;
theLock=[[NSLockalloc]init];
ticketsCondition=[[NSConditionalloc]init];
ticketsThreadone=[[NSThreadalloc]initWithTarget:selfselector:@selector(run)object:nil];
[ticketsThreadonesetName:@"Thread-1"];
[ticketsThreadonestart];
ticketsThreadtwo=[[NSThreadalloc]initWithTarget:selfselector:@selector(run)object:nil];
[ticketsThreadtwosetName:@"Thread-2"];
[ticketsThreadtwostart];
NSThread*ticketsThreadthree=[[NSThreadalloc]initWithTarget:selfselector:@selector(run3)object:nil];
[ticketsThreadthreesetName:@"Thread-3"];
[ticketsThreadthreestart];
self.window=[[UIWindowalloc]initWithFrame:[[UIScreenmainScreen]bounds]];
self.viewController=[[ViewControlleralloc]initWithNibName:@"ViewController"bundle:nil];
self.window.rootViewController=self.viewController;
[self.windowmakeKeyAndVisible];
returnYES;
}
-(void)run3{
while(YES){
[ticketsConditionlock];
[NSThreadsleepForTimeInterval:3];
[ticketsConditionsignal];
[ticketsConditionunlock];
}
}
-(void)run{
while(TRUE){
[ticketsConditionlock];
[ticketsConditionwait];
[theLocklock];
if(tickets>=0){
[NSThreadsleepForTimeInterval:0.09];
count=100-tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThreadcurrentThread]name]);
tickets--;
}else{
break;
}
[theLockunlock];
[ticketsConditionunlock];
}
}
wait是等待,我加了一个线程3去唤醒其他两个线程锁中的wait
其他同步
我们可以使用指令@synchronized来简化NSLock的使用,这样我们就不必显示编写创建NSLock,加锁并解锁相关代码。
-(void)doSomeThing:(id)anObj
{
@synchronized(anObj)
{
}
}