為什么要使用多線程? 學習多線程的目的: 將耗時操作放到后台去執行, 這也是學習多線程最主要的目的!
那么怎樣能看出哪些操作是耗時較多的呢,這里我們就來模擬一下內存幾個區不同的耗時情況:
假設有一個新聞類的app,如果我們按照在UI階段的方法,使用plist加載本地數據,那么這個app上的數據都是死的,用戶看來看去都是固定死的幾條“新聞”,最終的結果就是,用戶會刪掉這個app。
沒有數據的app猶如一潭死水,沒有生機!那么,怎么來實時地獲取數據呢?只有通過網絡從遠程服務器的數據庫中獲取實時數據。這樣我們的app才能夠保持活力!
但是,從網絡上獲取數據的時候會存在一個問題,比方說:下載一個小電影,通常是比較消耗時間的。也就是說,從網絡上獲取數據的操作屬於耗時操作。
那么,耗時操作會對我們的app產生什么影響呢?給大家提示一下,既然我們現在要學習多線程,那就說明我們之前寫的所有代碼都是在單線程上執行的。這里給大家舉個例子,過河,如果把河上的橋比作線程,那么我們之前都是在走獨木橋。走獨木橋有什么特點呢?假設有10個人要過河,但是第1個人張三跟人打架,腿瘸了,那么張三過橋就會非常墨跡。后面的人想過橋,沒門,必須等張三過去了,后面的人才可以過河。
這個例子放到程序里面,就是網絡操作比較耗時,如果網絡操作沒有執行完畢,用戶的其它操作就會被阻塞,結果就是用戶會感到非常卡頓,然后就是各種刪刪刪了。
而多線程就是專門用來解決這種問題的!
所以在引入多線程之前,我們先來做一個模擬耗時操作的演練。
1、代碼一:循環測試
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 單線程
[self demo];
}
#pragma mark - 模擬耗時操作
- (void)demo {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
}
NSLog(@"end");
}
打印台輸出結果為:
通過輸出結果可知:循環的速度非常非常快; 僅為0.025s
2、代碼二:操作內存的棧空間
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 單線程
[self demo];
}
#pragma mark - 模擬耗時操作
- (void)demo {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
int n = i;
}
NSLog(@"end");
}
打印台輸出結果為:
通過輸出結果可知:操作內存的棧空間,速度同樣非常快。僅為 0.026s
3、代碼三:操作內存的常量區
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 單線程
[self demo];
}
#pragma mark - 模擬耗時操作
- (void)demo {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
// 使用@""定義的字符串保存在常量區
NSString *str = @"hello";
}
NSLog(@"end");
}
打印台輸出結果為:
通過輸出結果可知: 操作內存的常量區, 速度比較快(比操作棧區稍微慢點) 0.099s
4、代碼四:操作內存的堆空間
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 單線程
[self demo];
}
#pragma mark - 模擬耗時操作
- (void)demo {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
// 使用 stringWithFormat 拼接的字符串保存在堆區
NSString *str = [NSString stringWithFormat:@"hello - %d", i];
}
NSLog(@"end");
}
打印台輸出結果為:
通過輸出結果可知:操作內存的堆空間,速度比操作常量區慢;循環非常消耗CPU資源: 時間為10.597s
5、代碼五:I/O操作
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 單線程
[self demo];
}
#pragma mark - 模擬耗時操作
- (void)demo {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
// I/O操作
NSLog(@"%d", i);
}
NSLog(@"end");
}
打印台輸出結果為:
從輸出結果可知:I/O操作,速度非常慢。
6、代碼六:引入多線程技術
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 多線程
[self performSelectorInBackground:@selector(demo) withObject:nil];
}
#pragma mark - 模擬耗時操作
- (void)demo {
NSLog(@"bengin");
for (int i = 0; i < 10000000; i++) {
// I/O操作
NSLog(@"%d", i);
}
NSLog(@"end");
}
打印台輸出結果為:
由輸出結果可知:引入多線程技術之后,即便是I/O操作這種耗時操作,也不會造成程序卡頓。
7、小結與思考
小結:
(1) 耗時操作的后果:如果只有主線程,會造成程序卡頓,用戶體驗極差。
(2) 學習多線程的目的:將耗時操作放到后台線程去執行。
(3) 通過耗時操作演練可知,操作效率的順序:
I/O操作 < 堆區 < 常量區 < 棧區。
(4) 使用@””定義的字符串保存在常量區,使用stringWithFormat拼接的字符串保存在堆區。
(5) 網絡操作也屬於耗時操作,通過多線程技術可以將耗時的網絡操作放到后台線程去執行,從而提高程序執行效率,改善用戶體驗。
(6) - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg 在后台執行某方法。
思考:
(1)耗時操作會對我們的應用程序產生什么影響?
耗時操作的后果:在主線程,耗時操作會造成程序卡頓,用戶會以為程序死了,用戶體驗極差。
(2)耗時操作造成的程序卡頓問題該怎么解決?
要想解決程序卡頓問題,就需要使用多線程技術,將耗時操作放到子線程去執行。
綜上所述,就可以看出多線程在我們實際開發中,是多么的重要!!!
更多內容請關注我的GitHub項目:
https://github.com/DXSmile/Multi-Thread-