iOS多線程詳解

/**

 無論使用哪種多線程技術都可以使用

 [NSThread currentThread]跟蹤查看當前執行所在的線程情況。

 num = 1表示在主線程上執行的任務

 

 ================================================================

 1. NSObject多線程技術

 

 1> 使用performSelectorInBackground可以開啓後臺線程,執行selector選擇器選擇的方法

 2> 使用performSelectorOnMainThread可以重新回到主線程執行任務,通常用於後臺線程更新界面UI時使用

 3> [NSThread sleepForTimeInterval:1.0f];

    讓當前線程休眠,通常在程序開發中,用於模擬耗時操作,以便跟蹤不同的併發執行情況!

 

    但是:在程序發佈時,千萬不要保留此方法!不要把測試中的代碼交給客戶,否則會造成不好的用戶體驗。

 

 提示:使用performSelectorInBackground也可以直接修改UI,但是強烈不建議使用。

 

 注意:在使用NSThread或者NSObject的線程方法時,一定要使用自動釋放池,否則容易出現內存泄露。

代碼演示:

(1)NSObject開闢子線程

 [self performSelectorInBackground:@selector(bigDemo)withObject:nil];

(2)用NSThread方法開闢子線程

   2.1// 類方法新建一個線程,調用@selector方法

   [NSThread detachNewThreadSelector:@selector(bigDemo)toTarget:self withObject:nil];

     2.2//成員方法

    NSThread *thread =        [[NSThread alloc]initWithTarget:self selector:@selector(bigDemo)object:nil];

    // 啓動start線程

    [thread start];

被子線程調用的方法:

- (void)bigDemo

{

    // 自動釋放池

    // 負責其他線程上的內存管理,在使用NSThread或者NSObject的線程方法時,一定要使用自動釋放池

    // 否則容易出現內存泄露。

    @autoreleasepool {

       

        NSLog(@"%@", [NSThread currentThread]);

        // 模擬網絡下載延時,睡眠1秒,通常是在開發中測試使用。

        [NSThread sleepForTimeInterval:1.0f];

        //強烈不建議直接在後臺線程更新界面UI

        // 模擬獲取到下載的圖像

        UIImage *image = [UIImage imageNamed:@"頭像1"];

        // 在主線程更新圖像

        // 使用self調用updateImage方法在主線程更新圖像

            [self performSelectorOnMainThread:@selector(updateImage:)withObject:image waitUntilDone:YES];

        // 使用imageViewsetImage方法在主線程更新圖像

        [_imageView performSelectorOnMainThread:@selector(setImage:)withObject:image waitUntilDone:YES];

        

    }

}

被主線程調用的方法:

- (void)updateImage:(UIImage *)image

{

    NSLog(@"更新圖像-> %@", [NSThread currentThread]);

    

    _imageView.image = image;

}

************************************************************

************************************************************

************************************************************

************************************************************

1GCD演練是基與C語言的NSOPeration&NSOperationQueue基於OC框架寫的 

• NSOperation的兩個子類NSInvocationOperation、NSBlockOperation

工作原理:NSOperation封裝要執⾏行的操作

將創建好的NSOperation對象放NSOperationQueue

啓動OperationQueue開始新的線程執⾏行隊列中的操作

1.1#pragma mark invocation

- (IBAction)operationDemo1

{

    NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(opAction)object:nil];

    

    // 如果使用start,會在當前線程啓動操作

//    [op1 start];

    

    // 1. 一旦將操作添加到操作隊列,操作就會啓動

    [_queue addOperation:op1];

}

#pragma mark - NSOperation演練

- (void)opAction

{

    NSLog(@"%@", [NSThread currentThread]);

    

    // 模擬網絡加載延時

    [NSThread sleepForTimeInterval:1.0f];

    

    // 模擬獲取到圖像

    UIImage *image = [UIImage imageNamed:@"頭像1"];

    

    // 設置圖像,在主線程隊列中設置

    [[NSOperationQueue mainQueueaddOperationWithBlock:^{

        _imageView.image = image;

    }];

}

、、、、、、、、、、、、、、、、、、、

1.2#pragma mark blockOperation

- (IBAction)operationDemo2

{

    // block的最大好處,可以將一組相關的操作,順序寫在一起,便於調試以及代碼編寫

    [_queue addOperationWithBlock:^{

        NSLog(@"%@", [NSThread currentThread]);

        

        // 模擬延時

        [NSThread sleepForTimeInterval:1.0f];

        

        // 模擬獲取到圖像

        UIImage *image = [UIImage imageNamed:@"頭像1"];

        

        // 設置圖像,在主線程隊列中設置

        [[NSOperationQueue mainQueueaddOperationWithBlock:^{

            _imageView.image = image;

        }];

    }];

}

//invocationblockOperation區別就是一個是用方法調用,一個是代碼塊調用


1.3NSOperation對象放NSOperationQueue牛逼之處


#pragma mark 依賴關係,可以使子線程按順序執行

- (IBAction)operationDemo3:(id)sender

{

     _queue=[[NSOperationQueue alloc]init];

    // 1. 下載

    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"下載 %@" , [NSThread currentThread]);

    }];

    // 2. 濾鏡

    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"濾鏡 %@" , [NSThread currentThread]);

    }];

    // 3. 顯示

    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"更新UI %@" , [NSThread currentThread]);

    }];

    

    // 添加操作之間的依賴關係,所謂依賴關係,就是等待前一個任務完成後,後一個任務才能啓動

    // 依賴關係可以跨線程隊列實現

    [op2 addDependency:op1];

    [op3 addDependency:op2];

    // 提示:在指定依賴關係時,注意不要循環依賴,否則不工作。

    //[op1 addDependency:op3];

    

    [_queue addOperation:op1];

    [_queue addOperation:op2];

    [[NSOperationQueue mainQueueaddOperation:op3];

}

1.4、最大併發線程數量,這是NSOperation獨有功能

- (IBAction)operationDemo4

{

    // 控制同時最大併發的線程數量

    [_queue setMaxConcurrentOperationCount:2];

    

    for (NSInteger i =0; i <200; i++) {

        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{

            NSLog(@"%@", [NSThread currentThread]);

        }];        

        [_queue addOperation:op];

    }

}

****************************************************************************************************************************************************************************************************************************************

********************************************************************************************************************

2GCD演練是基與C語言的框架寫的

- (IBAction)gcdDemo1

{

    /**

     GCD就是爲了在多核上使用多線程技術

     

     1> 要使用GCD,所有的方法都是dispatch開頭的

     2> 名詞解釋

        global  全局

        queue   隊列

        async   異步:執行控制不住先後順序

        sync    同步:主要用來控制方法的被調用的順序

     

     3> 要執行異步的任務,就在全局隊列中執行即可

        dispatch_async 異步執行控制不住先後順序

     

     4> 關於GCD的隊列

        全局隊列    dispatch_get_global_queue

            參數:優先級 DISPATCH_QUEUE_PRIORITY_DEFAULT

                 始終是 0

        串行隊列

        主隊列

     

     5> 異步和同步於方法名無關,與運行所在的隊列有關!

        提示:要熟悉隊列於同步、異步的運行節奏,一定需要自己編寫代碼測試!

     

        同步主要用來控制方法的被調用的順序

     */

    // 1. 隊列

   dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    

    // 2. 將任務異步(併發)執行

    dispatch_sync(queue, ^{

        NSLog(@"a->%@", [NSThread currentThread]);//同步在哪個線程,它執行的方法就在哪個線程

    });

    dispatch_async(queue, ^{

        NSLog(@"b->%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

        dispatch_sync(queue, ^{

            NSLog(@"我哦我哦我->%@", [NSThread currentThread]);

        });

        //"我哦我哦我"所在的線程就是這個子線程不不不不不不不不不所在的線程

        NSLog(@"不不不不不不不不不->%@", [NSThread currentThread]);

    });

    //GCD回到主線程的時候必須是異步的

    dispatch_async(dispatch_get_main_queue(), ^{

        NSLog(@"main - > %@", [NSThread currentThread]);

    });//dispatch_sync方法不能在主隊列中調用,因爲這會無限期的阻止線程並會導致你的應用死鎖。所有通過GCD提交到主隊列的任務必須是異步的。


}

2.1#pragma mark 串行隊列,裏面的操作按順序執行

create方法,獲得的是自己新建的線程,所以create需要我們釋放線程(如果是手動管理內存的話)

 //dispatch_queue_t用來引導隊列變量

    //createQueue 就是一個隊列變量

    //dispatch_queue_create是手動創建隊列的方法,兩個參數,第一個參數是這個隊列的名,第二個參數如果寫DISPATCH_QUEUE_SERIAL表示這是一個順序的隊列,這個隊列裏的方法順序執行。如果是DISPATCH_QUEUE_CONCURRENT,表示這是一個並行的隊列,則這個隊列裏的方法同時執行

    

       //create出來的默認爲default優先級

    

    //用完之後,要釋放

    //dispatch_release(createQueue);

- (void)makeGCD_Queue

{

    //串行隊列自行創建,不能get

    dispatch_queue_t queue=dispatch_queue_create("Myqueue",DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{

        for (int i =0 ;i<1000; i++) {

            NSLog(@"1");

        }

        NSLog(@"a->%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

        NSLog(@"b->%@", [NSThread currentThread]);

    });

    dispatch_async(queue, ^{

        for (int i =0 ;i<1000; i++) {

            NSLog(@"2");

        }

        NSLog(@"c->%@", [NSThread currentThread]);

    });

    //GCD回到主線程的時候必須是異步的

    dispatch_async(queue, ^{

        //必須將這個回到主線程的方法,放在這個隊列中的一個請求方法,纔會按照順序執行

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"d->%@", [NSThread currentThread]);

            

        });//dispatch_sync方法不能在主隊列中調用,因爲這會無限期的阻止線程並會導致你的應用死鎖。所有通過GCD提交到主隊列的任務必須是異步的。

        

    });

}

2.2#pragma mark GCD隊列組,併發執行所有任務,所有任務執行完畢後回主隊列

- (void)makeGCD_Group {

    dispatch_group_t group =dispatch_group_create();

    dispatch_queue_t queue =dispatch_get_global_queue(0,0);

    dispatch_group_async(group, queue, ^{

        for (int i =0 ;i<1000; i++) {

            NSLog(@"1");

        }

        NSLog(@"a->%@", [NSThread currentThread]);

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@“b->%@“, [NSThread currentThread]);

    });

    dispatch_group_async(group, queue, ^{

        for (int i =0 ;i<1000; i++) {

            NSLog(@"2");

        }

        NSLog(@“c->%@“, [NSThread currentThread]);

    });

    //Group隊列任務組,併發執行任務組中的任務,全部執行完畢後,再最後用notify告知所有任務完成,並做相應處理

    dispatch_group_notify(group,dispatch_get_main_queue(), ^{

        NSLog(@“d->%@“, [NSThread currentThread]);

    });

    

}


3全局隊列和併發隊列的區別:

    1、全局隊列沒有名字,但是併發隊列有名字。有名字可以便於查看系統日誌

    2、全局隊列是所有應用程序共享的。

    3、在mrc的時候,全局隊列不用手動釋放,但是併發隊列需要。

    

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