天天看點

IOS并發線程學習(XCode9.1 Object-C)

第一部分:連結

  1. iOS 多線程:『pthread、NSThread』詳盡總結
  2. .iOS 多線程:『GCD』詳盡總結
  3. OS 多線程:『NSOperation、NSOperationQueue』詳盡總結
  4. iOS 多線程:『RunLoop』詳盡總結
  5. 關于performSelector:afterDelay:的一個坑及思考

第二部分:例子

1.NSThread

-(void)downloadImageOnSub{
    [NSThread detachNewThreadSelector:@selector(downLoadImage) toTarget:self withObject:nil];
}

-(void)downLoadImage{
    NSLog(@"Current Thread:%@", [NSThread currentThread]);
    NSURL *imgUrl = [NSURL URLWithString:@"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=414435988,70382748&fm=15&gp=0.jpg"];
    NSData *imgData = [NSData dataWithContentsOfURL:imgUrl];
    UIImage *img = [UIImage imageWithData:imgData];
    [self performSelectorOnMainThread:@selector(updateImageView:) withObject:img waitUntilDone:YES];
}

-(void)updateImageView:(UIImage*)img{
    NSLog(@"Current Thread:%@", [NSThread currentThread]);
    mImageView.image = img;
}
           

更新UI時切換到主線程:performSelectorOnMainThread,這部分跟Android很類似。這裡稍微提醒下,IOS預設隻支援HTTPS,不支援HTTP,原因時安全問題。

2. NSNotification

-(void)registerNotify {
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(doNotify) name:@"mynotify" object:nil];
    [[NSNotificationCenter defaultCenter]addObserverForName:@"myMainNotify" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"Recieve main Notify:%@",[NSThread currentThread]);
    }];
}

- (IBAction)sendNotify:(id)sender {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   [[NSNotificationCenter defaultCenter]postNotificationName:@"mynotify" object:nil];
   });
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [[NSNotificationCenter defaultCenter]postNotificationName:@"myMainNotify" object:nil];
    });
}

-(void)doNotify{
    NSLog(@"Recieve notify:%@", [NSThread currentThread]);
}
           

這裡有兩種register的方法,addObserverForName提供了切換到主線程分方法。

  1. NSRunLoop

    1)運作示意圖

    IOS并發線程學習(XCode9.1 Object-C)
    2)NSTimer
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
}
-(void)run{
    NSLog(@"run test1");
}
           

運作效果:使用者不操作時會不斷列印"run test1",如使用者點選滑鼠則拖動滾動條之類,則不會運作run,NSDefaultRunLoopMode相當于Android的idle狀态。

3). 圖檔延遲顯示

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"Touch Begin");
    [self performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"loop.jpg"] afterDelay:4.0 inModes:@[NSDefaultRunLoopMode]];
}

-(void)setImage:(UIImage *)image{
    NSLog(@"setImage:%@", image);
    self.mImageView.image = image;
}
           

運作效果:滑鼠松開後4秒後顯示指定圖檔,注意這裡的代碼與參考連接配接中不一樣,參考連接配接中用self.imageView performSelector,是運作不了,具體原因沒分析過,估計是self.imageView導緻線程切換到非主線程了。

4)常駐記憶體線程

@property (strong, nonatomic) NSThread *mThread;
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.    
    self.mThread = [[NSThread alloc]initWithTarget:self selector:@selector(run2) object:nil];
    [self.mThread start];
}
-(void)run2{
    //add task here
    NSLog(@"run test2");
    
    //start the loop
    [[NSRunLoop currentRunLoop]addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop]run];
    NSLog(@"test2 end");
}
           

運作發現,隻會列印run test2,不會列印end,這就好比Android中的Loop循環。如果不好了解,再加入如下代碼運作多次。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"Touch Begin");
    [self performSelector:@selector(run) onThread:self.mThread withObject:nil waitUntilDone:NO];
}
           

多次點選滑鼠,就會發現隻會列印run test1,不會列印run test2,因為Loop第一次就已經起來,後面隻管執行task即可。