iOS多線程篇-多線程實(shí)現(xiàn)之NSThread

NSThread基本概念

  • 一個(gè)NSThread對(duì)象就是代表一條線程
    • 一個(gè)NSThread線程對(duì)象都可以有它對(duì)應(yīng)的名字和編號(hào)(number).
      • 名字可以通過(guò)NSThread的name屬性來(lái)設(shè)置
      • 編號(hào)(number)是不能設(shè)置的,一般由系統(tǒng)決定,但主線程默認(rèn)就是1,只要不為1的就是子線程
    • 線程是有調(diào)度優(yōu)先級(jí)的,取值范圍(0.0~1.0),默認(rèn)為0.5,取值越大,優(yōu)先級(jí)越大.通過(guò)threadPriority屬性來(lái)設(shè)置優(yōu)先級(jí)
  • 線程的狀態(tài)(生命周期)
    • 1>新創(chuàng)建一個(gè)線程是,線程是處于新建狀態(tài)
    • 2>啟動(dòng)線程之后,線程就處于就緒狀態(tài),并且會(huì)把線程放入CPU中得可調(diào)度線程池中
    • 3>當(dāng)CPU調(diào)度到線程之后,線程就處于運(yùn)行狀態(tài).CPU調(diào)度其他線程話,線程又會(huì)處于就緒狀態(tài).(所以說(shuō)線程會(huì)在就緒狀態(tài)和運(yùn)行狀態(tài)間來(lái)回切換)
    • 4>當(dāng)線程被sleep或遇到鎖的時(shí)候,線程會(huì)處于阻塞狀態(tài),并且會(huì)把線程移出CPU中的可調(diào)度線程池.
    • 5>當(dāng)線程sleep完或鎖解開的時(shí)候,會(huì)把線程放回CPU中的可調(diào)度線程池,繼續(xù)在就緒狀態(tài)和運(yùn)行狀態(tài)間來(lái)回切換
    • 6>線程中的任務(wù)執(zhí)行完畢或者異常/強(qiáng)制退出的時(shí)候,系統(tǒng)就會(huì)把線程release(釋放掉).
  • 在啰嗦一次,任何和UI相關(guān)的操作必須在主線程中執(zhí)行.

廢話不多說(shuō),上代碼

//
//  ViewController.m
//
//  Created by 雨軒 on 16/1/23.
//  Copyright ? 2016年 apple. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

/**
 *  屏幕觸摸事件
 *
 */
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    //獲取當(dāng)前線程
    NSThread * currentThread = [NSThread currentThread];

    //判斷調(diào)用此方法的線程對(duì)象 是否是主線程
//    [currentThread isMainThread];
    //判斷當(dāng)前線程是否是主線程
//    [NSThread isMainThread];

    //打印結(jié)果發(fā)現(xiàn)當(dāng)前線程是主線程
    //前面說(shuō)過(guò):  和UI相關(guān)操作都必須放在主線程中.屏幕觸摸也屬于UI相關(guān)操作
    NSLog(@"當(dāng)前線程:    %@",currentThread);
    
    //獲取主線程
    NSThread * mainThread = [NSThread mainThread];
    
    //打印當(dāng)前線程 和主線程
    //打印結(jié)果發(fā)現(xiàn)兩個(gè)內(nèi)存地址是一樣的: 由此可證明一個(gè)進(jìn)程里有且只會(huì)有一條主線程
    NSLog(@"%@ ====== %@",currentThread, mainThread);
    
    
    
    [self createNSThreadOne];
    //    [self createNSThreadTwo];
    //    [self createNSThreadThree];
    
    //線程間的通信
    //在子線程中下載圖片
    //給imageView設(shè)置圖片也數(shù)據(jù)UI操作,所以必須在主線程中設(shè)置
    //就得在子線程中告訴主線程,讓它去設(shè)置圖片
    [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
    
}

//耗時(shí)操作
-(void)operate:(NSString *)object{
    
    NSLog(@"%@",object);
    NSThread * currentThread = [NSThread currentThread];
    for(int i = 0; i < 50000 ; i++){
       
        //讓當(dāng)前線程睡眠2秒
        //[NSThread sleepForTimeInterval:2.0];
        //讓當(dāng)前線程睡眠到指定時(shí)間
        //NSDate * date = [NSDate dateWithTimeIntervalSinceNow:2.0]; //獲取當(dāng)前時(shí)間后兩秒的時(shí)間
        //[NSThread sleepUntilDate:date];
        NSLog(@"%d========%@",i,currentThread);
        if(i == 30){
            NSLog(@"線程強(qiáng)制退出");
            //強(qiáng)制退出當(dāng)前線程(線程進(jìn)入死亡狀態(tài)),線程不在執(zhí)行任何任務(wù)
            //如果在鎖內(nèi)使用,會(huì)把鎖內(nèi)所有子線程退出
            [NSThread exit];
        }
    }
    
    //如果執(zhí)行了[NSThread exit] 就不會(huì)執(zhí)行這行代碼了
    // 就相當(dāng)于 return 出這個(gè)方法了.
    NSLog(@"任務(wù)執(zhí)行完畢");
    
}

//NSThread 創(chuàng)建子線程方式一
-(void)createNSThreadOne{
    
    /*
     創(chuàng)建一個(gè)子線程: 此創(chuàng)建方式需要手動(dòng)啟動(dòng)線程
     參數(shù)說(shuō)明:
     第一個(gè)參數(shù): 目標(biāo)對(duì)象
     第二個(gè)參數(shù): 執(zhí)行目標(biāo)對(duì)象中那個(gè)方法
     第三個(gè)參數(shù): 執(zhí)行方法需要傳入的參數(shù), 沒有參數(shù)寫nil即可
     */
    //從代碼上來(lái)看,好像這個(gè)線程是個(gè)局部變量,看起來(lái)好像方法執(zhí)行完之后就會(huì)銷毀這個(gè)局部線程
    //其實(shí)并不會(huì),因?yàn)槿绻趫?zhí)行系統(tǒng)分離出來(lái)的子線程的話,系統(tǒng)內(nèi)部會(huì)retain一次
    //只有線程中的方法執(zhí)行完畢之后,系統(tǒng)會(huì)自動(dòng)release(釋放)掉此子線程
    NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(operate:) object:@"AAA"];
    
    //設(shè)置線程名稱
    thread.name = @"線程AAA";
    
    //設(shè)置線程優(yōu)先級(jí)(0.0~0.1)
    thread.threadPriority = 0.7;
    
    //啟動(dòng)線程
    [thread start];
}

//NSThread 創(chuàng)建子線程方式二
-(void)createNSThreadTwo{
    
    /*
     創(chuàng)建一個(gè)子線程: 此方法創(chuàng)建線程無(wú)需手動(dòng)啟動(dòng), 系統(tǒng)創(chuàng)建好之后會(huì)自動(dòng)啟動(dòng)線程
     缺點(diǎn): 無(wú)法詳細(xì)設(shè)置線程
     */
    [NSThread detachNewThreadSelector:@selector(operate:) toTarget:self withObject:@"BBB"];
}

//NSThread 創(chuàng)建子線程方式三
-(void)createNSThreadThree{
    
    //使用此方法,系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè)子線程,然后在子線程中調(diào)用此方法
    //特點(diǎn):自動(dòng)啟動(dòng)線程,但不能詳細(xì)設(shè)置線程
    [self performSelectorInBackground:@selector(operate:) withObject:@"CCC"];
}

//線程間的通信
-(void)downloadImage{
    
    //圖片地址
    NSURL * imageUrl = [NSURL URLWithString:@"http://www.xxjxsj.cn/article/UploadPic/2009-8/2009810023722867.jpg"];
    
    //加載圖片數(shù)據(jù)
    NSData * imageData = [NSData dataWithContentsOfURL:imageUrl];
    
    //創(chuàng)建圖片
    UIImage * image = [UIImage imageWithData:imageData];
    
    NSLog(@"%@",[NSThread currentThread]);
    //通信方式一
    //指定對(duì)象的方法在主線程中調(diào)用,這里用了個(gè)小技巧 直接調(diào)用了imageView中的 setImage: 方法
    ///waitUntilDone:是否等待執(zhí)行完成
    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
    
    //通信方式二
    //指定對(duì)象的某個(gè)方法去指定的線程里執(zhí)行
    //waitUntilDone:是否等待執(zhí)行完成
    //[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容