每一個iOS應用程序都有個專門用來更新顯示UI界面、處理用戶的觸摸事件的主線程,所以不能將其餘太耗時的操做放在主線程中執行,否則會形成主線程堵塞(出現卡機現象),帶來極壞的用戶體驗。通常的解決方案就是將那些耗時的操做放到另一個線程中去執行,多線程編程是防止主線程堵塞,增長運行效率的最佳方法java
iOS支持多個層次的多線程編程,層次越高的抽象程度越高,使用也越方便,也是蘋果最推薦使用的方法。下面根據抽象層次從低到高依次列出iOS所支持的多線程編程方法:編程
1.Thread :是三種方法裏面相對輕量級的,但須要管理線程的生命週期、同步、加鎖問題,這會致使必定的性能開銷
2.Cocoa Operations:是基於OC實現的,NSOperation以面向對象的方式封裝了須要執行的操做,沒必要關心線程管理、同步等問題。NSOperation是一個抽象基類,iOS提供了兩種默認實現:NSInvocationOperation和NSBlockOperation,固然也能夠自定義NSOperation
3.Grand Central Dispatch(簡稱GCD,iOS4纔開始支持):提供了一些新特性、運行庫來支持多核並行編程,它的關注點更高:如何在多個cpu上提高效率
多線程
這篇文章簡單介紹了第一種多線程編程的方式,主要是利用NSThread這個類,一個NSThread實例表明着一條線程app
1、NSthread的初始化ide
1.動態方法性能
[java] view plaincopyatom
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument; spa
[java] view plaincopy.net
// 初始化線程 線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 設置線程的優先級(0.0 - 1.0,1.0最高級)
thread.threadPriority = 1;
// 開啓線程
[thread start];
參數解析:
selector :線程執行的方法,這個selector最多隻能接收一個參數
target :selector消息發送的對象
argument : 傳給selector的惟一參數,也能夠是nil
2.靜態方法
[java] view plaincopy
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
[java] view plaincopy
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// 調用完畢後,會立刻建立並開啓新線程
3.隱式建立線程的方法
[java] view plaincopy
[self performSelectorInBackground:@selector(run) withObject:nil];
2、獲取當前線程
[java] view plaincopy
NSThread *current = [NSThread currentThread];
3、獲取主線程
[java] view plaincopy
NSThread *main = [NSThread mainThread];
4、暫停當前線程
[java] view plaincopy
// 暫停2s
[NSThread sleepForTimeInterval:2];
// 或者
NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]];
[NSThread sleepUntilDate:date];
5、線程間的通訊
1.在指定線程上執行操做
[java] view plaincopy
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];
2.在主線程上執行操做
[java] view plaincopy
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
3.在當前線程執行操做
[java] view plaincopy
[self performSelector:@selector(run) withObject:nil];
6、優缺點
1.優勢:NSThread比其餘兩種多線程方案較輕量級,更直觀地控制線程對象
2.缺點:須要本身管理線程的生命週期,線程同步。線程同步對數據的加鎖會有必定的系統開銷
--------------------------------------------
--------------另一個博客的補充----------
咱們演示一個經典的賣票的例子來說NSThread的線程同步:
.h
[cpp] view plaincopy
#import <UIKit/UIKit.h>
@class ViewController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
int tickets;
int count;
NSThread* ticketsThreadone;
NSThread* ticketsThreadtwo;
NSCondition* ticketsCondition;
NSLock *theLock;
}
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) ViewController *viewController;
@end
[cpp] view plaincopy
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
tickets = 100;
count = 0;
theLock = [[NSLock alloc] init];
// 鎖對象
ticketsCondition = [[NSCondition alloc] init];
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)run{
while (TRUE) {
// 上鎖
// [ticketsCondition lock];
[theLock lock];
if(tickets >= 0){
[NSThread sleepForTimeInterval:0.09];
count = 100 - tickets;
NSLog(@"當前票數是:%d,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);
tickets--;
}else{
break;
}
[theLock unlock];
// [ticketsCondition unlock];
}
}
若是沒有線程同步的lock,賣票數多是-1.加上lock以後線程同步保證了數據的正確性。
上面例子我使用了兩種鎖,一種NSCondition ,一種是:NSLock。 NSCondition我已經註釋了。
他們均可以經過
[ticketsCondition signal]; 發送信號的方式,在一個線程喚醒另一個線程的等待。
好比:
[cpp] view plaincopy
#import "AppDelegate.h"
#import "ViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
tickets = 100;
count = 0;
theLock = [[NSLock alloc] init];
// 鎖對象
ticketsCondition = [[NSCondition alloc] init];
ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadone setName:@"Thread-1"];
[ticketsThreadone start];
ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[ticketsThreadtwo setName:@"Thread-2"];
[ticketsThreadtwo start];
NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];
[ticketsThreadthree setName:@"Thread-3"];
[ticketsThreadthree start];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
-(void)run3{
while (YES) {
[ticketsCondition lock];
[NSThread sleepForTimeInterval:3];
[ticketsCondition signal];
[ticketsCondition unlock];
}
}
- (void)run{
while (TRUE) {
// 上鎖
[ticketsCondition lock];
[ticketsCondition wait];
[theLock lock];
if(tickets >= 0){
[NSThread sleepForTimeInterval:0.09];
count = 100 - tickets;
NSLog(@"當前票數是:%d,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);
tickets--;
}else{
break;
}
[theLock unlock];
[ticketsCondition unlock];
}
}
wait是等待,我加了一個 線程3 去喚醒其餘兩個線程鎖中的wait
咱們可使用指令 @synchronized 來簡化 NSLock的使用,這樣咱們就沒必要顯示編寫建立NSLock,加鎖並解鎖相關代碼。- (void)doSomeThing:(id)anObj{ @synchronized(anObj) { // Everything between the braces is protected by the @synchronized directive. }}