iOS執行緒間通訊
阿新 • • 發佈:2019-01-09
什麼叫做執行緒間通訊
- 在1個程序中,執行緒往往不是孤立存在的,多個執行緒之間需要經常進行通訊
執行緒間通訊的體現
- 1個執行緒傳遞資料給另1個執行緒
- 在1個執行緒中執行完特定任務後,轉到另1個執行緒繼續執行任務
執行緒間通訊常用方法
1. NSThread :一個執行緒傳遞資料給另一個執行緒
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait; - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0); //具體用法如下: //點選螢幕開始執行下載方法 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self performSelectorInBackground:@selector(download) withObject:nil]; } //下載圖片 - (void)download { // 1.圖片地址 NSString *urlStr = @"http://d.jpg"; NSURL *url = [NSURL URLWithString:urlStr]; // 2.根據地址下載圖片的二進位制資料 NSData *data = [NSData dataWithContentsOfURL:url]; // 3.設定圖片 UIImage *image = [UIImage imageWithData:data]; // 4.回到主執行緒,重新整理UI介面(為了執行緒安全) [self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO]; // [self performSelector:@selector(downloadFinished:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES]; } - (void)downloadFinished:(UIImage *)image { self.imageView.image = image; NSLog(@"downloadFinished---%@", [NSThread currentThread]); }
2. GCD :一個執行緒傳遞資料給另一個執行緒
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"donwload---%@", [NSThread currentThread]); // 1.子執行緒下載圖片 NSURL *url = [NSURL URLWithString:@"http://d.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; // 2.回到主執行緒設定圖片 dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"setting---%@ %@", [NSThread currentThread], image); [self.button setImage:image forState:UIControlStateNormal]; }); }); }
3. NSMachPort
NSPort有3個子類,NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。
#define kMsg1 100 #define kMsg2 101 - (void)viewDidLoad { [super viewDidLoad]; //1. 建立主執行緒的port // 子執行緒通過此埠傳送訊息給主執行緒 NSPort *myPort = [NSMachPort port]; //2. 設定port的代理回撥物件 myPort.delegate = self; //3. 把port加入runloop,接收port訊息 [[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode]; //4. 啟動次執行緒,並傳入主執行緒的port MyWorkerClass *work = [[MyWorkerClass alloc] init]; [NSThread detachNewThreadSelector:@selector(launchThreadWithPort:) toTarget:work withObject:myPort]; } - (void)handlePortMessage:(NSMessagePort*)message{ NSLog(@"接到子執行緒傳遞的訊息!%@",message); //1. 訊息id NSUInteger msgId = [[message valueForKeyPath:@"msgid"] integerValue]; //2. 當前主執行緒的port NSPort *localPort = [message valueForKeyPath:@"localPort"]; //3. 接收到訊息的port(來自其他執行緒) NSPort *remotePort = [message valueForKeyPath:@"remotePort"]; if (msgId == kMsg1) { //向子線的port傳送訊息 [remotePort sendBeforeDate:[NSDate date] msgid:kMsg2 components:nil from:localPort reserved:0]; }else if (msgId == kMsg2){ NSLog(@"操作2....\n"); } }
//MyWorkerClass類
#import "MyWorkerClass.h"
@interface MyWorkerClass() <NSMachPortDelegate> {
NSPort *remotePort;
NSPort *myPort;
}
@end
#define kMsg1 100
#define kMsg2 101
@implementation MyWorkerClass
- (void)launchThreadWithPort:(NSPort *)port {
@autoreleasepool {
//1. 儲存主執行緒傳入的port
remotePort = port;
//2. 設定子執行緒名字
[[NSThread currentThread] setName:@"MyWorkerClassThread"];
//3. 開啟runloop
[[NSRunLoop currentRunLoop] run];
//4. 建立自己port
myPort = [NSPort port];
//5.
myPort.delegate = self;
//6. 將自己的port新增到runloop
//作用1、防止runloop執行完畢之後推出
//作用2、接收主執行緒傳送過來的port訊息
[[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];
//7. 完成向主執行緒port傳送訊息
[self sendPortMessage];
}
}
/**
* 完成向主執行緒傳送port訊息
*/
- (void)sendPortMessage {
NSMutableArray *array =[[NSMutableArray alloc]initWithArray:@[@"1",@"2"]];
//傳送訊息到主執行緒,操作1
[remotePort sendBeforeDate:[NSDate date]
msgid:kMsg1
components:array
from:myPort
reserved:0];
}
#pragma mark - NSPortDelegate
/**
* 接收到主執行緒port訊息
*/
- (void)handlePortMessage:(NSPortMessage *)message
{
NSLog(@"接收到父執行緒的訊息...\n");
}
@end