1.多線程的基本概念和用法
①進程:每一個進程都一是一個應用程序,都有獨立的內存空間。(一個程序也可以由多個進程)同一個進程中的線程共享內存中的存儲空間和資源
②線程:
每一個程序都有一個主線程,調用main來啓動。
主線程的生命週期和應用程序綁定,程序退出時主線程停止。
任何可以阻塞主線程的任務不要再主線程中執行(比如訪問網絡)
2.多線程使用的注意
①線程使用不是無節制的:
iOS中的主線程的堆棧大小是1M(不可變)
從第二個線程開始都是512KB(不可變)
②只有主線程可以修改UI,渲染引擎工作在主線程中
3.IOS的三種多線程技術(本文主題)
①NSThread:每個NSThread對象對應一個線程
優:量級較輕
缺:需要自己管理線程的生命週期、線程同步、加鎖、睡眠和喚醒等,
②NSOperation/NSOperationQueue:面向對象的線程技術
優:不需要關心線程管理、數據同步的事情,且是面向對象的
③GCD(Grand Central Dispatch):是基於C語言的框架,可以充分利用多核,是蘋果推薦使用的多線程技術
下面對三種技術,用代碼逐一講解:
①NSThread
- (void)viewDidLoad {
[super viewDidLoad];
for (int i=0; i<10; i++) {
//1.線程睡眠
[NSThread sleepForTimeInterval:1];
NSLog(@"主線程1: %d", i);
}
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(thread1:) object:@"multiThread1"];
//2.開始線程
[thread start];
for (int i=0; i<10; i++) {
//3.獲取當前線程
NSThread *current = [NSThread currentThread];
//4.判斷當前線程是否是主線程
BOOL isMain = [current isMainThread];
if (isMain) {
NSLog(@"主線程2: %d", i);
}
}
}
-(void) thread1:(NSString *) threadName{
for (int i=0; i<10; i++) {
NSLog(@"%@: %d", threadName, i);
if (i == 5) {
//5.退出線程
[NSThread exit];
}
}
}
②NSOperationQueue
- (void)viewDidLoad {
[super viewDidLoad];
//1.創建線程隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.創建線程任務
/*NSOperation介紹:
是Cocoa中的一個抽象類,用來封裝單個任務和代碼執行一項操作,由於是抽象類,所以不能直接實例化使用,必須定義子類繼承該抽象類來實現(常用的爲NSInvocationOperation類,但是也可以自己繼承重寫NSOperation類)
*/
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread1:) object:@"thread1"];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread2:) object:@"thread2"];
//3.設置隊列屬性
//1)設置併發數量
queue.maxConcurrentOperationCount = 1;
//2)暫停線程
queue.suspended = YES;
//3)設置線程優先級,需要先暫停線程纔會生效
op1.queuePriority = NSOperationQueuePriorityHigh;
op2.queuePriority = NSOperationQueuePriorityVeryLow;
//4.任務完成時調用block
op1.completionBlock = ^{
NSLog(@"op1已經完成");
};
//5.創建任務的另一種方法:block方式
[queue addOperationWithBlock:^{
//也是一個線程入口,和op1、op2一樣是一個線程
for (int k=0; k<10; k++) {
NSLog(@"blockThread: %d", k);
}
}];
[queue addOperation:op2];
[queue addOperation:op1];
//6.開始線程
queue.suspended = NO;
for (int k=0; k<10; k++) {
NSLog(@"main: %i", k);
}
}
-(void) thread1:(NSString *) threadName {
for (int i=0; i<10; i++) {
NSLog(@"%@: %d", threadName, i);
}
}
-(void) thread2:(NSString *) threadName {
for (int j=0; j<10; j++) {
NSLog(@"%@: %d", threadName, j);
}
}
③GCD
傳送門:http://blog.csdn.net/u012526801/article/details/49004871
補充1:線程中的內存管理問題,NSRunloop和如何跳出線程
NSRunloop:
①線程的生命週期存在5個狀態:新建、就緒、運行、阻塞、死亡
②NSRunLoop可以保持一個線程一直爲活動狀態,不會馬上銷燬掉
③應用舉例:多線程中使用定時器時,必須使用Runloop。因爲只有開啓Runloop保持線程爲活動狀態,定時器纔不會失效跳出線程方法:
調用cancel方法,然後再線程方法中根據線程的isCancelled屬性可以break出線程方法
①跳出單個線程:[thread1 cancel];
②隊列中,跳出多個線程:[queue cancelAllOperations];內存問題:
多線程創建的實例對象不會放入自動釋放池,可能會導致內存泄露。需要手動放入自動重生池
舉例:創建一個線程隊列queue和一個線程類MyOperation,在queue中添加2個繼承於MyOperation類的任務,實現以上補充內容中提到的知識點
以下是viewController中的代碼:
- (void)viewDidLoad {
[super viewDidLoad];
_queue = [[NSOperationQueue alloc] init];
_op1 = [[MyOperation alloc] initwithName:@"thread1"];
_op2 = [[MyOperation alloc] initwithName:@"thread2"];
[_queue addOperation:_op1];
[_queue addOperation:_op2];
_op1.completionBlock = ^ {
NSLog(@"op1已經完成------------------");
};
_op2.completionBlock = ^ {
NSLog(@"op2已經完成------------------");
};
// [self performSelector:@selector(afterDelay) withObject:nil afterDelay:0.1];
[self performSelector:@selector(cancelAllThread) withObject:nil afterDelay:0.5];
}
-(void) afterDelay {
[_op1 cancel];
}
-(void) cancelAllThread {
[_queue cancelAllOperations];
}
以下是MyOperation類的具體實現:
#import <Foundation/Foundation.h>
@interface MyOperation : NSOperation
@property (nonatomic, copy) NSString *mask;
-(instancetype) initwithName:(NSString *)mask;
@end
//##############################################################
#import "MyOperation.h"
@implementation MyOperation
-(instancetype) initwithName:(NSString *)mask {
if (self == [super init]) {
_mask = mask;
}
return self;
}
-(void) main {
//1.多線程創建的實例對象不會放入自動釋放池,可能會導致內存泄露。需要手動放入自動重生池
@autoreleasepool {
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeAction) userInfo:nil repeats:YES];
//2.開啓Runloop來保持線程的存活狀態
[[NSRunLoop currentRunLoop] run];
for (int i=0; i<1000; i++) {
//3.cancel方法只是改變isCancelled屬性,在線程實現方法中做判斷,用於跳出線程方法。
if (self.isCancelled) {
break;
}
NSLog(@"%@: %d", self.mask, i);
}
}
}
-(void) timeAction {
}
@end