NSThread
- 父類是NSObject
(1)NSThread的創(chuàng)建
//第一種創(chuàng)建線程的方式:alloc initWithTarget.
//特點(diǎn):需要手動(dòng)開(kāi)啟線程蛇券,可以拿到線程對(duì)象進(jìn)行詳細(xì)設(shè)置
//創(chuàng)建線程
/*
第一個(gè)參數(shù):目標(biāo)對(duì)象
第二個(gè)參數(shù):選擇器漱挎,線程啟動(dòng)要調(diào)用哪個(gè)方法
第三個(gè)參數(shù):前面方法要接收的參數(shù)(最多只能接收一個(gè)參數(shù),沒(méi)有則傳nil)
*/
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"wendingding"];
//啟動(dòng)線程
[thread start];
//第二種創(chuàng)建線程的方式:分離出一條子線程
//特點(diǎn):自動(dòng)啟動(dòng)線程侧巨,無(wú)法對(duì)線程進(jìn)行更詳細(xì)的設(shè)置
/*
第一個(gè)參數(shù):線程啟動(dòng)調(diào)用的方法
第二個(gè)參數(shù):目標(biāo)對(duì)象
第三個(gè)參數(shù):傳遞給調(diào)用方法的參數(shù)
*/
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是分離出來(lái)的子線程"];
//第三種創(chuàng)建線程的方式:后臺(tái)線程
//特點(diǎn):自動(dòng)啟動(dòng)線程,無(wú)法對(duì)線程進(jìn)行更詳細(xì)設(shè)置
[self performSelectorInBackground:@selector(run:) withObject:@"我是后臺(tái)線程"];
//第四種創(chuàng)建線程的方式:alloc init
//特點(diǎn):任務(wù)封裝在自定義對(duì)象的main方法中习贫,不暴露
NSThread *thread = [[NSThread alloc]init];
//啟動(dòng)任務(wù)
[thread start];
(2)設(shè)置線程的屬性
//設(shè)置線程的屬性
//設(shè)置線程的名稱
thread.name = @"線程A";
//設(shè)置線程的優(yōu)先級(jí),注意線程優(yōu)先級(jí)的取值范圍為0.0~1.0之間略贮,1.0表示線程的優(yōu)先級(jí)最高,如果不設(shè)置該值,那么理想狀態(tài)下默認(rèn)為0.5
thread.threadPriority = 1.0;
(3)線程的狀態(tài)(了解)
線程的狀態(tài).png
//常用的控制線程狀態(tài)的方法
[thread start];//進(jìn)入就緒狀態(tài)茧跋,等待運(yùn)行
[NSThread sleepForTimeInterval:2.0];//阻塞線程
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];//阻塞線程
[NSThread exit];//強(qiáng)制退出當(dāng)前線程
//注意:線程死了不能復(fù)生,線程死了與線程對(duì)象是否釋放無(wú)關(guān)(可用成員屬性強(qiáng)指針指向某線程卓囚,線程對(duì)象存在瘾杭,但線程任務(wù)完成時(shí)依然會(huì)“死”)
//通過(guò)break,return提前結(jié)束任務(wù),是正常死亡而非強(qiáng)制讓線程死亡
(4)線程安全
線程安全.png
互斥鎖.png
01 前提:多個(gè)線程訪問(wèn)同一塊資源會(huì)發(fā)生數(shù)據(jù)安全問(wèn)題哪亿,“同一塊資源”:比如同一個(gè)對(duì)象粥烁、同一個(gè)變量、同一個(gè)文件
02 解決方案:加互斥鎖
2.1 實(shí)質(zhì):通過(guò)線程同步蝇棉,使同一塊資源在同一時(shí)間只能被一個(gè)線程訪問(wèn)页徐,其余線程等待訪問(wèn)
2.2 優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
2.3 缺點(diǎn):需要消耗大量的CPU資源
03 相關(guān)代碼:@synchronized(self){}
3.1 鎖定1份代碼只用1把鎖,用多把鎖是無(wú)效的
3.2 鎖對(duì)象一般使用self
@synchronized(鎖對(duì)象) { // 需要鎖定的代碼 }
04 專業(yè)術(shù)語(yǔ)-線程同步
4.1 線程同步即多條線程在同一條線上執(zhí)行(按順序地執(zhí)行任務(wù))
4.2 互斥鎖使用了線程同步技術(shù)
05 原子和非原子屬性(主要是對(duì)setter方法加鎖)
5.1 OC在定義屬性時(shí)有nonatomic和atomic兩種選擇
5.1-1 atomic:原子屬性银萍,為setter方法加鎖(默認(rèn)就是atomic)变勇;線程安全,需要消耗大量的資源
5.1-2 nonatomic:非原子屬性贴唇,不會(huì)為setter方法加鎖搀绣;非線程安全,適合內(nèi)存小的移動(dòng)設(shè)備
5.2 iOS開(kāi)發(fā)的建議
5.2-1 所有屬性都聲明為nonatomic
5.2-2 盡量避免多線程搶奪同一塊資源
5.2-3 盡量將加鎖戳气、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理链患,減小移動(dòng)客戶端的壓力
(5)線程間通信
01 在1個(gè)進(jìn)程中,線程往往不是孤立存在的瓶您,多個(gè)線程之間需要經(jīng)常進(jìn)行通信
02 線程間通信的體現(xiàn)
2-1 1個(gè)線程傳遞數(shù)據(jù)給另1個(gè)線程
2-2 在1個(gè)線程中執(zhí)行完特定任務(wù)后麻捻,轉(zhuǎn)到另1個(gè)線程繼續(xù)執(zhí)行任務(wù)
03 線程間通信方式 – 利用NSPort(沒(méi)有講,知道即可呀袱,暫不用深究)
-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
//開(kāi)啟一條子線程來(lái)下載圖片
[NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}
-(void)downloadImage
{
//1.確定要下載網(wǎng)絡(luò)圖片的url地址贸毕,一個(gè)url唯一對(duì)應(yīng)著網(wǎng)絡(luò)上的一個(gè)資源
NSURL *url = [NSURL URLWithString:@"http://p6.qhimg.com/t01d2954e2799c461ab.jpg"];
//2.根據(jù)url地址下載圖片數(shù)據(jù)到本地(二進(jìn)制數(shù)據(jù)
NSData *data = [NSData dataWithContentsOfURL:url];
//3.把下載到本地的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成圖片
UIImage *image = [UIImage imageWithData:data];
//4.回到主線程刷新UI
//4.1 第一種方式
/*
第一個(gè)參數(shù):要調(diào)用的方法名稱
第二個(gè)參數(shù):方法要接受的參數(shù)
第三個(gè)參數(shù):要不要等待,YES表示等待,NO不等待
*/
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
//4.2 第二種方式(簡(jiǎn)便方法)
//setImage:是imageView自身的方法
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
//4.3 第三種方式
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}
//4.4-4.5 第四、五種方式 與循環(huán)有關(guān)的方式
// [self.imageView performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#> modes:<#(nullable NSArray<NSString *> *)#>]
// [self.imageView performSelector:<#(nonnull SEL)#> onThread:<#(nonnull NSThread *)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#> modes:<#(nullable NSArray<NSString *> *)#>];
// [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"Snip20151105_143"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode,UITrackingRunLoopMode]];
(6)如何計(jì)算代碼段的執(zhí)行時(shí)間
//第一種方法
NSDate *start = [NSDate date];
//2.根據(jù)url地址下載圖片數(shù)據(jù)到本地(二進(jìn)制數(shù)據(jù))
NSData *data = [NSData dataWithContentsOfURL:url];
NSDate *end = [NSDate date];
NSLog(@"第二步操作花費(fèi)的時(shí)間為%f",[end timeIntervalSinceDate:start]);
//第二種方法
//獲取啟動(dòng)時(shí)間
CFTimeInterval start = CFAbsoluteTimeGetCurrent();
NSData *data = [NSData dataWithContentsOfURL:url];
//獲取結(jié)束時(shí)間
CFTimeInterval end = CFAbsoluteTimeGetCurrent();
//打印用時(shí)
NSLog(@"第二步操作花費(fèi)的時(shí)間為%f",end - start);//獲取的時(shí)間都是絕對(duì)時(shí)間夜赵,可以直接相減
(7)主線程其他相關(guān)用法(可直接查閱文檔)
+ (NSThread *)mainThread; // 獲得主線程
+ (NSThread *)currentThread;//獲得當(dāng)前線程
+ (BOOL)isMultiThreaded;//判斷是否是多線程
+ (BOOL)isMainThread; // 判斷是否為主線程
- (BOOL)isMainThread; // 判斷是否為主線程
//獲取線程的可變字典明棍,只讀
thread.threadDictionary
//判斷線程狀態(tài)
//是否執(zhí)行
@property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0);
//是否完成
@property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0);
//是否取消
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);