呃...

其實不會有什麼精深的東西,也就只是把我學習過程記錄一下...

2014年8月17日 星期日

NSNotificationCenter

同樣是很久前就解決的問題...積了不少文章 囧

這個類別在我剛接觸時其實有稍微困擾過,後來再姑狗上看到了同是Stanford的教學卻有附註解釋,真是容易懂太多了,不常用到時會經常忘記,就發這篇記錄一下囉。

透過defaultCenter這個+(method)來alloc與init一個NSNotificationCenter instance,在調用下方的-(method)即可實現監視物件活動的方法。

NSNotificationCenter *center=[NSNotificationCenter defaultCenter];
addObserver - 誰來收通知(通常是self)
selector    - 當收到通知時被調用的方法
name        - 決定監視該發出通知的情況類型,通常是已經預先define好的字串
object      - 被監視的對象

NSNotificationCenter

- (void)addObserver:(id)observer // you (the object to get notified)
selector:(SEL)methodToInvokeIfSomethingHappens
name:(NSString *)name 
// name of station (a constant somewhere)
object:(id)sender; // whose changes you’re interested in (nil is anyone’s) You will then be notified when there are broadcasts

You will then be notified when there are broadcasts 
- (void)methodToInvokeIfSomethingHappens:(NSNotification *)notification
{
notification.name 
// the name passed above
notification.object // the object sending you the notification notification.userInfo // notification-specific information about what happened
}

How to implement serial execution of GCD queue?(group, barrier, serial)


廢話time:
原本打算是從上一篇文章開始都用英文撰寫來練習英文寫作,但是打完上一篇文章後卻覺得,繁體中文資源已經不多,很多技術文件或討論文件在中文搜尋上根本是零零落落或乾脆搜尋不到相關資料,因此還是改變決定,繼續以中文撰寫,算是勉強盡一份心力於中文資源。

積存已久的問題,雖然早已解決,但是GCD的部分還是沒有很熟,還需要了解NSOperationQueue與NSThread的差別,現在主要在熟悉CS193p的文件,等未來有空再發一篇文章補齊三者的使用時機。

在main thread 使用NSURLConnection,建立一個thread等待資料庫讀完,再回到main thread執行一些code,在program裡面看起來如下:

//part.1
[NSURLConnection *myDB=[...];
//概要來說是使用POST Method, 送NSURLrequest以及設定delegate為self, 
//在NSURLconnection的protocol - connection didRecievedata實現一個loadprogress

//part.2
     dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
     dispatch_async(queue,^{
     while (myDB.loadprogress!=1){
          //這裡只是等待progress=1, 就是讓data收完
}
     dispath_async(dispatch_get_main_queue, ^{
          //data收完,回到main thread去做事,例如轉換格式,取資料,填資料到tableview等
     });
});

//part.3
//這邊繼續做main thread的事

現在問題在於,假使我希望part3在某個片段可以等待part2完成(收data)再繼續做事,比較好的寫法是什麼?

Solution:
GCD有group、serial或barrier的建立方式,都可以實現等待上個thread完成後再執行的效果。以下是一些簡單的implement:

barrier - 如同barrier的意思,會擋住下游的thread執行直到上游的thread執行完畢以及本身thread執行完畢,方讓下游的thread繼續執行。
   
    dispatch_queue_t queue=dispatch_queue_create("flamelad", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"concurrent:%d\n",i);
        }
    });
    dispatch_barrier_async(queue, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"barrier:%d\n",i);
        }
    });
    dispatch_async(queue, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"concurrent1:%d\n",i);
        }
    });
//    輸出結果:
//    2014-08-17 01:58:42.464 Sherlock[2836:1303] concurrent:0
//    2014-08-17 01:58:42.465 Sherlock[2836:1303] concurrent:1
//    2014-08-17 01:58:42.466 Sherlock[2836:1303] concurrent:2
//    2014-08-17 01:58:42.467 Sherlock[2836:1303] barrier:0
//    2014-08-17 01:58:42.468 Sherlock[2836:1303] barrier:1
//    2014-08-17 01:58:42.469 Sherlock[2836:1303] barrier:2
//    2014-08-17 01:58:42.470 Sherlock[2836:1303] concurrent1:0
//    2014-08-17 01:58:42.470 Sherlock[2836:1303] concurrent1:1
//    2014-08-17 01:58:42.471 Sherlock[2836:1303] concurrent1:2
 
serial- 與concurrent不同(很明顯吧XD),queue中的每一個thread會循序處理,FIFO
    dispatch_queue_t serial=dispatch_queue_create("flamelad", NULL);
    dispatch_async(serial, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"serial:%d\n",i);
        }
    });
    dispatch_async(serial, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"serial2:%d\n",i);
        }
    });
    dispatch_async(serial, ^{
        for (int i=0; i<3; i++) {
            NSLog(@"serial3:%d\n",i);
        }
    });
//    輸出結果
//    2014-08-17 02:19:43.616 Sherlock[2898:1303] serial:0
//    2014-08-17 02:19:43.626 Sherlock[2898:1303] serial:1
//    2014-08-17 02:19:43.643 Sherlock[2898:1303] serial:2
//    2014-08-17 02:19:43.644 Sherlock[2898:1303] serial2:0
//    2014-08-17 02:19:43.646 Sherlock[2898:1303] serial2:1
//    2014-08-17 02:19:43.648 Sherlock[2898:1303] serial2:2
//    2014-08-17 02:19:43.649 Sherlock[2898:1303] serial3:0
//    2014-08-17 02:19:43.650 Sherlock[2898:1303] serial3:1
//    2014-08-17 02:19:43.651 Sherlock[2898:1303] serial3:2
group - group有notify和wait方式可以實現類似barrier的效果,不同處在於可以做一些細節上的設定。從輸出結果即可知wait會等待上游thread完成,notify則會等到group中所有thread完成。
    dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group=dispatch_group_create();
    dispatch_group_async(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group:%d\n",i);
        }
    });
    dispatch_group_async(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group2:%d\n",i);
        }
    });
//    可設定等待下游的thread等待上游thread多久時間後開始執行,FOREVER則是等到上游執行完成後。
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_group_async(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group3:%d\n",i);
        }
    });
    dispatch_group_notify(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group4:%d\n",i);
        }
    });
    dispatch_group_async(group,queue,^{
        for (int i=0; i<3; i++) {
            NSLog(@"group5:%d\n",i);
        }
    });
//    輸出結果
//    2014-08-17 02:32:54.587 Sherlock[3001:130b] group:0
//    2014-08-17 02:32:54.587 Sherlock[3001:3c07] group2:0
//    2014-08-17 02:32:54.588 Sherlock[3001:130b] group:1
//    2014-08-17 02:32:54.588 Sherlock[3001:3c07] group2:1
//    2014-08-17 02:32:54.590 Sherlock[3001:130b] group:2
//    2014-08-17 02:32:54.591 Sherlock[3001:3c07] group2:2
//    2014-08-17 02:32:54.591 Sherlock[3001:130b] group3:0
//    2014-08-17 02:32:54.592 Sherlock[3001:3c07] group5:0
//    2014-08-17 02:32:54.593 Sherlock[3001:130b] group3:1
//    2014-08-17 02:32:54.593 Sherlock[3001:3c07] group5:1
//    2014-08-17 02:32:54.594 Sherlock[3001:130b] group3:2
//    2014-08-17 02:32:54.595 Sherlock[3001:3c07] group5:2
//    2014-08-17 02:32:54.596 Sherlock[3001:3c07] group4:0
//    2014-08-17 02:32:54.597 Sherlock[3001:3c07] group4:1
//    2014-08-17 02:32:54.598 Sherlock[3001:3c07] group4:2

這篇只介紹了三種最常用到的GCD thread執行方式來解決我的問題,但其實尚有些如dispatch_once、dispatch_semaphore_t等的執行方式,有興趣者再自行姑狗,都是在實現multi-thread石,可能會用到的方法。