IOS學習——GCD和後臺處理

突然接觸了IOS,來到了一個瑰麗的世界,沒想到剛開始工作用到的就是大學唯一一丁點都沒有接觸的東西,sad!

抒情打住……

OC也是第一次接觸,so~~這麼一隻菜鳥,使用了Apress兩本經典作爲入門基礎:David Mark《精通IOS開發》+Scott Knaster《Objective-C》。這兩本書,水果味的,嘿!之前的筆記都記在了“有道”上,記得不詳細,所以冒出在博客上寫的想法,可能會督促自己認真一些,邊學習邊記錄了。代碼的例子用教材中的,然後加上自己的註釋和理解。

GCD(grand central dispatch): 底層隊列(多線程優化技術)
這項技術到底應該如何理解我還要看更多官方文檔纔會有自己的理解,這裏直接記錄學習過程

1.代碼塊

代碼塊這個概念在Java中只是在靜態塊static{  }記得有這麼個名詞,沒想到在OC和swift中運用如此頻繁!在Swift中,又稱爲閉包,不誇張地將,閉包是Swift的一等公民,可以將閉包賦值給變量、傳遞給方法或者作爲調用的返回結果。閉包和等價於OC中的代碼塊,代碼塊十分有助於GCD的應用。

<pre name="code" class="objc">//聲明一個返回值爲空,參數爲空的塊變量
void (^loggerBlock)(void)
 
__block int a=0;
int b=1;
loggerBlock^{
<span style="white-space:pre">	</span>a=9;
<span style="white-space:pre">	</span>nslog(@"%d",b);
}
//執行
loggerBlock();


注意:塊內可以訪問在塊外定義的變量,如b,但是隻能是當塊被創建時,這些變量被複制(普通類型int float)或者被保存(指向對象的指針)在塊中不可以被更改的同名變量中。就是說,代碼塊中的b是外面的b複製過來的。
如果要想在塊中對塊外定義的變量進行寫操作,如對a重新賦值,則要把變量a定義爲__black int類型!!!
而且這個類型在代碼塊中被使用後不會被複制和保留。

2.GCD處理

*爲了讓某個方法在後臺運行,只需要
    1、將所有代碼包裝在一個代碼塊(Swift稱爲閉包)中;
    2、將該代碼塊傳遞給GCD函數dispatch_async函數。

dispatch_async ( param1 , param2 )
    param1: 一個GCD隊列
    param2:代碼塊

用法:
//1.聲明GCD隊列    
dispatch_queue_t    globalQueue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT/HIGH/LOW);
//2.實現希望後臺運行的代碼塊
dispatch_async ( globalQueue , ^{

    //do something  in 後臺線程

     3//得到希望的數據後,回到主線程進行更新
       dispatch_async (dispatch_get_main_queue() , ^{

            do something   in 主線程 
    });

});


下面是教材中的一個例子,這個程序是在在文本框中輸入字符串,按下start button,顯示字符串的長度,超簡單!只是在計算字符串長度的時候用sleep一會兒,所以只用主線程來做的話,頁面會一直處於掛起(向Android卡死一樣)狀態,實際上系統在等着sleep的時間過了,在實際的應用中,用戶就會覺得這程序崩了,點擊什麼的全都無效了所以就用到了GCD來處理這個問題,使程序可以不卡在這裏:
-(void)doWork:(id)sender{

    self.resultsTextView.text = @"";
    NSDate *startTime = [NSDate date];
    //後臺處理的時候button不可按,並且出現加載動畫
    self.startButton.enabled = NO;
    [self.spinner startAnimating];
    
    dispatch_queue_t  globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(globalQueue, ^{
        @try{
        NSString *fetchedData = [self fetchSomethingFromServer];
        NSString *processData = [self processData:fetchedData];
        NSString *firstData  = [self calculateFirstResult:processData];
            NSString *secondData = [self calculateSecondResult:processData];
            NSString *resultData = [NSString stringWithFormat:@"%@ \n %@",firstData,secondData];
        //update in main queue
        dispatch_async(dispatch_get_main_queue(), ^{
           
            self.resultsTextView.text = resultData;
            self.startButton.enabled = YES;
            [self.spinner stopAnimating];
        
        });
        
        NSDate *endTime = [NSDate date];
        NSLog(@"Complete in %f time",[endTime timeIntervalSinceDate:startTime]);
        }@catch (NSException *e){
            NSLog(@"%@",e);
        }
    });
}

如果沒有拉回主線程的操作,由於後臺想GUI更新數據是不可能的,所以運行到刷新數據的語句後,程序會crash,下面是通過@try @catch 獲得的錯誤信息,主要是通信錯誤:


接下來我不得不說,下面這個設計太讓人舒服了!!!
上面後臺處理的解決方案只是爲了讓程序因爲sleepForTimeInterval 而處於掛起模式,但是雖然在底層運行,但我們在時間上還沒有進行優化,即:用時還是各方法sleep的總時間, 那麼最好的解決方法就是將沒有數據干擾的部分並行執行,提高代碼的執行速度:這就用到了  分派組 (dispatch group)    可以將需要並行的幾塊代碼放在分派組代碼塊中,這樣各個組內的任務就可以併發執行了!!!
然而,組與組之間處理任務的快慢不同,如果最後的數據更新涉及到所有組,如何判斷所有組的任務全部完成了呢?接下來就是真正令我激動的地方——dispatch_group_notify()  
這個函數配合分配組隊列使用,指定一個額外的代碼塊,讓這部分代碼塊在所有組的任務全部執行完畢後再執行

用法:
dispatch_queue_t    globalQueue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT/HIGH/LOW);
dispatch_async ( globalQueue , ^{

     dispatch_group_t group = dispatch_group_create();
     dispatch_group_async(group , queue,^{
                function1();
    });
     dispatch_group_async(group , queue,^{
                function2();
    });
    dispatch_async_notify(group , queue  , ^{    

         dispatch_async (dispatch_get_main_queue() , ^{

                do something   in 主線程 
            });
    });

});
運用到例子中就是
<pre name="code" class="objc">-(void)doWork2:(id)sender{
    
    self.resultsTextView.text = @"";
    NSDate *startTime = [NSDate date];
    //後臺處理的時候button不可按,並且出現加載動畫
    self.startButton.enabled = NO;
    self.startButton.alpha = 0.5;
    [self.spinner startAnimating];
    
    dispatch_queue_t  queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
    
            NSString *fetchedData = [self fetchSomethingFromServer];
            NSString *processData = [self processData:fetchedData];
            //NSString *firstData  = [self calculateFirstResult:processData];
            //NSString *secondData = [self calculateSecondResult:processData];
            //NSString *resultData = [NSString stringWithFormat:@"%@ \n %@",firstData,secondData];
            __block NSString *firstData;//need to be assigned in block
            __block NSString *secondData;
            dispatch_group_t group = dispatch_group_create();
            dispatch_group_async(group, queue, ^{
<span style="white-space:pre">		</span>firstData = [self calculateFirstResult:processData];//firstData需在塊內進行賦值,所以要聲明成__block變量,兩個"_"哦
            });
            dispatch_group_async(group, queue, ^{ 
                secondData = [self calculateSecondResult:processData];
            });
            
            dispatch_group_notify(group, queue, ^{
                
                NSString *resultData = [NSString stringWithFormat:@"%@\n%@",firstData,secondData];
                
                //update in main queue
                dispatch_async(dispatch_get_main_queue(), ^{
                
                    self.resultsTextView.text = resultData;
                    self.startButton.enabled = YES;
                    self.startButton.alpha = 1.0;
                    [self.spinner stopAnimating];
                
                });
                
                NSDate *endTime = [NSDate date];
                NSLog(@"Complete in %f time",[endTime timeIntervalSinceDate:startTime]);
            });    
        }
    });
}



寫不下去了,明天再補吧,回家睡覺




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章