關於GCD NSOperation

作者:Love@YR
鏈接:http://blog.csdn.net/jingqiu880905/article/details/51839499
請尊重原創,謝謝!

建議先弄清同步異步 串行並行區別:參考這篇文章
dispatch_queue_t 串行並行隊列 相當於NSOperationQueue
dispatch_async/sync 裏block構成的異步/同步任務 相當於NSOperation

ios7.0之後NSOperation的isConcurrent被deprecated,改成了isAsynchronous,改完後跟上面對應。

NSOperation:
https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationObjects/OperationObjects.html#//apple_ref/doc/uid/TP40008091-CH101-SW1

官方文檔 Table 2-2前面那段話描述說:
Operation objects execute in a synchronous manner by default—that is, they perform their task in the thread that calls their start method. Because operation queues provide threads for nonconcurrent operations, though, most operations still run asynchronously. However, if you plan to execute operations manually and still want them to run asynchronously, you must take the appropriate actions to ensure that they do. You do this by defining your operation object as a concurrent operation.

即NSOperation對象默認情況下是同步執行的。同步意味着阻塞。意思即此Operation是在調用它start方法的那個線程裏執行其任務的。(所以說你在主線程創建一個operation,然後並不重寫這個operation的start方法,直接調用其start方法的話就會使得此任務直接在主線程裏執行。)

但是由於operation queues 是會自動爲加在其裏面的非併發operation創建線程的(這部分代碼我們看不到,應該是addOperation方法裏取出此operation然後new了一個子線程再執行這個operation的start方法),所以即使你的operation設置的isConcurrent爲NO(或者未重寫isConcurrent方法),把operation加入operation queue此operation也會異步執行。

所以說如果你不想把operation加入operation queue裏,即你要手動去調用其start方法,然後你還想讓它異步執行的話,你就需要自己在代碼裏實現了。

後來我碰到一個問題,因爲AFNetWorking的請求操作是異步的,AFURLConnectionOperation的isConcurrent是YES,但是我想同步執行一個請求,就是想拿到請求結果再走下面的代碼怎麼辦呢?它的start方法又自己創建了子線程。我怎樣能在主線程裏同步執行這個請求呢?
兩句:1. 調用start 2.調用 waitUntilFinished。
因爲waitUntilFinished保證了阻塞當前線程而等待操作完成即把這個操作改成了同步的。
可以參考:http://www.kwstu.com/ArticleView/guandebao_20139616530941

還有就是一般爲operation創建線程的事情都是由operation queue去做,那麼AFURLConnectionOperation 爲什麼要在start方法裏自己創建子線程呢?看到了這篇文章:
http://blog.sina.com.cn/s/blog_74e9d98d0101h1hk.html

還有一個問題就是一個operation是代表一個線程嗎?
答案:不是。

那麼一個operation是代表一個任務嗎?
答案:也不是。

參考:http://www.cnblogs.com/wendingding/p/3809042.html
這篇文章的NSBlockOperation例子可以說明operation只是一系列動作的集合。這一系列動作可以併發執行。可以在不同的線程裏。

爲了更直觀地說明任務 線程 操作之間的關係,我們看下面的代碼:

- (IBAction)buttonTaped:(id)sender {
    dispatch_queue_t myQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(myQueue, ^{
        for (NSInteger i = 0; i < 500000000; i++) {
            if (i == 0) {
                NSLog(@"a1s1 for循環->開始%@",[NSThread currentThread]);
            }
            if (i == 499999999) {
                NSLog(@"a1s1 for循環->完成");
            }
        }
        dispatch_async(myQueue, ^{
            for (NSInteger i = 0; i < 500000000; i++) {
                if (i == 0) {
                    NSLog(@"a1a1 -> 開始%@",[NSThread currentThread]);
                }
                if (i == 499999999) {
                    NSLog(@"a1a1 -> 完成");
                }
            }});

        for (NSInteger n = 0; n < 2; n++) {
            dispatch_sync(myQueue, ^{
                for (NSInteger i = 0; i < 500000000; i++) {
                    if (i == 0) {
                        NSLog(@"a1s%ld-> 開始%@",(long)n+1,[NSThread currentThread]);
                    }
                    if (i == 499999999) {
                        NSLog(@"a1s%ld -> 完成",(long)n+1);
                    }
                }
            });
        }
        dispatch_async(myQueue, ^{
            for (NSInteger i = 0; i < 500000000; i++) {
                if (i == 0) {
                    NSLog(@"a1a2 -> 開始%@",[NSThread currentThread]);
                }
                if (i == 499999999) {
                    NSLog(@"a1a2 -> 完成");
                }
            }});
    });

    dispatch_async(myQueue, ^{
        for (NSInteger i = 0; i < 500000000; i++) {
            if (i == 0) {
                NSLog(@"a2-> 開始%@",[NSThread currentThread]);
            }
            if (i == 499999999) {
                NSLog(@"a2-> 完成");
            }
        }});

    NSLog(@"阻塞我沒有?當前線程%@",[NSThread currentThread]);
}

運行結果:

2016-07-06 17:29:58.316 threadDemo[1241:185626] a2-> 開始<NSThread: 0x7fdbcad065e0>{number = 3, name = (null)}

2016-07-06 17:29:58.316 threadDemo[1241:185464] 阻塞我沒有?當前線程<NSThread: 0x7fdbcac01280>{number = 1, name = main}

2016-07-06 17:29:58.316 threadDemo[1241:185642] a1s1 for循環->開始<NSThread: 0x7fdbcae1a6a0>{number = 2, name = (null)}

2016-07-06 17:29:59.594 threadDemo[1241:185642] a1s1 for循環->完成

2016-07-06 17:29:59.594 threadDemo[1241:185626] a2-> 完成

2016-07-06 17:29:59.594 threadDemo[1241:185642] a1s1-> 開始<NSThread: 0x7fdbcae1a6a0>{number = 2, name = (null)}

2016-07-06 17:29:59.594 threadDemo[1241:185626] a1a1 -> 開始<NSThread: 0x7fdbcad065e0>{number = 3, name = (null)}

2016-07-06 17:30:00.849 threadDemo[1241:185642] a1s1 -> 完成

2016-07-06 17:30:00.850 threadDemo[1241:185642] a1s2-> 開始<NSThread: 0x7fdbcae1a6a0>{number = 2, name = (null)}

2016-07-06 17:30:00.860 threadDemo[1241:185626] a1a1 -> 完成

2016-07-06 17:30:02.134 threadDemo[1241:185642] a1s2 -> 完成

2016-07-06 17:30:02.134 threadDemo[1241:185642] a1a2 -> 開始<NSThread: 0x7fdbcae1a6a0>{number = 2, name = (null)}

2016-07-06 17:30:03.418 threadDemo[1241:185642] a1a2 -> 完成

其中a的意思是異步,s爲同步。
上述例子表明:
1. 我們可以在block任務裏嵌套任務
2. 每個子任務並不代表一個線程。
我們可以看出來,在並行隊列中:
異步的block需要等待其前面一個同步的block做完才能開始,同步的block不需要等待其前面一個異步block可以和它同時開始,同步的block需要等待其前面一個同步的block做完才能開始。
幾個異步block可以同時開始。
而任務運行的線程數目和任務沒有關係,會在運行時由cpu調度。之前以爲並行隊列裏多少任務就多少線程其實是錯的,如果前面一個任務做完了,其線程會拿來執行後面的任務。就如上面的例子,只開了2個子線程。

推薦幾篇好文章:
http://www.cnblogs.com/yjg2014/p/yjg.html
關於GCD與NSOperation的區別:
http://www.jianshu.com/p/d09e2638eb27
http://blog.csdn.net/q199109106q/article/details/8566222

———關於延時/定時的補充:————————-

 //重複執行
 -(void)someMethod{
 static dispatch_source_t _timer;
    //間隔還是60秒
    uint64_t interval = 60 * NSEC_PER_SEC;
    //創建一個專門執行timer回調的GCD隊列
    dispatch_queue_t queue = dispatch_queue_create("uploadScore", 0);
    //創建Timer
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //使用dispatch_source_set_timer函數設置timer參數
    dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
    //設置回調
    dispatch_source_set_event_handler(_timer, ^()
    {
        [uploadscoreVC startUploadScore];
    });
    //dispatch_source默認是Suspended狀態,通過dispatch_resume函數開始它
    dispatch_resume(_timer);
}

—————————原文—————————————–
下面說下 iOS中延時的幾種方法:

//非阻塞 執行一次  
//delayMethod在dispatch_after指定的線程裏執行
- (void)methodFourGCD{
    __block GCDViewController *weakSelf = self;
    dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
//    dispatch_after(delayTime, dispatch_get_main_queue(), ^{
//        [weakSelf delayMethod];
//    });
    dispatch_after(delayTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        [weakSelf delayMethod];
    });
}



//阻塞 delayMethod在所在線程執行,所以最好不要放在主線程裏
- (void)methodThreeSleep{
    [NSThread sleepForTimeInterval:2.0];
    [self delayMethod];
}



//非阻塞 delayMethod主線程執行,其他線程無效
- (void)methodTwoNSTimer{
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];
}



//非阻塞 delayMethod主線程執行,其他線程無效
- (void)methodOnePerformSelector{
    [self performSelector:@selector(delayMethod) withObject:nil afterDelay:2.0];
}

NSTimer 可以用invalidate去取消,其他的暫無取消方法。
NSTimer還可以用設置fireDate的方法去暫停(distantFuture)和重啓(sincenow)

關於用NSThread去創建一個子線程的幾種方法:

 NSThread *myThread = [[NSThread alloc]initWithTarget:self selector:@selector(startCount) object:nil];
    [myThread start];

    //靜態方法創建
    [NSThread detachNewThreadSelector:@selector(startCount) toTarget:self withObject:nil];

    //隱式創建
    [self performSelectorInBackground:@selector(startCount) withObject:nil];

其中detachNewThreadSelector即創建完立即start

發佈了56 篇原創文章 · 獲贊 28 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章