1. 程式人生 > >iOS執行緒間通訊

iOS執行緒間通訊

什麼叫做執行緒間通訊

  •  在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