iOS block從零開始

來源:互聯網
上載者:User

標籤:使用   問題   block   任務   return   fine   dha   exp   data   

iOS block從零開始

在iOS4.0之後,block橫空出世,它本身封裝了一段代碼並將這段代碼當做變數,通過block()的方式進行回調。

block的結構

先來一段簡單的代碼看看:

void (^myBlock)(int a) = ^(int a){        NSLog(@"%zd",a);    };    NSLog(@"旭寶愛吃魚");    myBlock(999);

輸出結果:

2016-05-03 11:27:18.571 block[5340:706252] 旭寶愛吃魚
2016-05-03 11:27:18.571 block[5340:706252] 999

下面我們解析一下:

  • void :返回的參數 void為沒有傳回值
  • (^myBlock):myBlock 為 block 的名稱
  • (int a):此為參數列表
  • ^(int a):傳入參數

通過上面的簡單介紹可以簡單瞭解到block的結構那麼下面便產生了四種格式的block。

四種block

有傳回值無參:

int (^myBlock)() = ^(){        return 999;    };

有傳回值有參:

int (^myBlock)(int a) = ^(int a){        NSLog(@"%zd",a);        return a;    };

無傳回值無參:

void (^myBlock)() = ^(){        NSLog(@"旭寶愛吃魚");    };

無傳回值有參:

void (^myBlock)(int a) = ^(int a){        NSLog(@"%zd",a);    };
block 捕獲外界局部變數

範例程式碼:

int a = 10;    void (^myBlock)() = ^(){        NSLog(@"旭寶愛吃魚");        NSLog(@"%zd",a);    };    NSLog(@"旭寶愛吃魚");    myBlock();

運行結果:

2016-05-03 11:43:32.680 block[5406:713702] 旭寶愛吃魚
2016-05-03 11:43:32.681 block[5406:713702] 旭寶愛吃魚
2016-05-03 11:43:32.681 block[5406:713702] 10

通過範例程式碼不難發現我們可以擷取局部變數,那麼改變局部變數是否也會改變block內的值呢。

範例程式碼:

int a = 10;    void (^myBlock)() = ^(){        NSLog(@"旭寶愛吃魚");        NSLog(@"%zd",a);    };    a = 20;    NSLog(@"旭寶愛吃魚");    myBlock();

運行結果:

2016-05-03 11:47:07.669 block[5425:715861] 旭寶愛吃魚
2016-05-03 11:47:07.670 block[5425:715861] 旭寶愛吃魚
2016-05-03 11:47:07.670 block[5425:715861] 10

正如結果顯示,block內部的值是不會改變的,為什麼呢????

範例程式碼:

int a = 10;    a = 20;    void (^myBlock)() = ^(){        NSLog(@"旭寶愛吃魚");        NSLog(@"%zd",a);    };    NSLog(@"旭寶愛吃魚");    myBlock();

運行結果:

2016-05-03 11:49:19.749 block[5450:717309] 旭寶愛吃魚
2016-05-03 11:49:19.750 block[5450:717309] 旭寶愛吃魚
2016-05-03 11:49:19.750 block[5450:717309] 20

情理之中的答案。
之所以block內部的值不會改變是因為block copy了局部變數。

這個問題解決後嘗試在block中改變局部變數。
很不幸失敗了,當我在block內改變外界的局部變數時,報錯了。
可視化我想改變外界的局部變數我該怎麼辦呢???

改變外界的局部變數

範例程式碼:

__block int a = 10;    void (^myBlock)() = ^(){        NSLog(@"旭寶愛吃魚");        NSLog(@"%zd",a);        a = 20;    };    NSLog(@"旭寶愛吃魚");    myBlock();    NSLog(@"%zd",a);

運行結果:

2016-05-03 11:55:02.736 block[5490:721033] 旭寶愛吃魚
2016-05-03 11:55:02.737 block[5490:721033] 旭寶愛吃魚
2016-05-03 11:55:02.737 block[5490:721033] 10
2016-05-03 11:55:02.737 block[5490:721033] 20

我們只需要在局部變數前加__block 即可。

循環參考(我在前面的部落格有所提及)

block在iOS開發中被視作是對象,因此其生命週期會一直等到持有人的生命週期結束了才會結束。另一方面,由於block捕獲變數的機制,使得持有block的對象也可能被block持有,從而形成循環參考,導致兩者都不能被釋放:

@implementation CXObject{   void (^_cycleReferenceBlock)(void);}- (void)viewDidLoad{    [super viewDidLoad];    _cycleReferenceBlock = ^{         NSLog(@"%@", self);   //引發循環參考    };}@end

遇到這種代碼編譯器只會告訴你存在警告,很多時候我們都是忽略警告的,這最後會導致記憶體泄露,兩者都無法釋放。跟普通變數存在block關鍵字一樣的,系統提供給我們weak的關鍵字用來修飾物件變數,聲明這是一個弱引用的對象,從而解決了循環參考的問題:

__weak typeof(*&self) weakSelf = self;_cycleReferenceBlock = ^{     NSLog(@"%@", weakSelf);   //弱指標引用,不會造成循環參考};
使用block

在block出現之前,開發人員實現回調基本都是通過代理的方式進行的。比如負責網路請求的原生類NSURLConnection類,通過多個協議方法實現請求中的事件處理。而在最新的環境下,使用的NSURLSession已經採用block的方式處理工作要求了。各種第三方網路請求架構也都在使用block進行回調處理。這種轉變很大一部分原因在於block使用簡單,邏輯清晰,靈活等原因。接下來我會完成一次網路請求,然後通過block進行回調處理。這些回調包括請求完成、下載進度

按照returnValue(^blockName)(parameters)的方式進行block的聲明未免麻煩了些,我們可以通過關鍵字typedef來為block起類型名稱,然後直接通過類型名進行block的建立:

@interface CXDownloadManager: NSObject//block重新命名typedef void(^CXDownloadHandler)(NSData * receiveData, NSError * error);typedef void(^CXDownloadProgressHandler)(CGFloat progress);- (void)downloadWithURL: (NSString *)URL parameters: (NSDictionary *)parameters handler: (CXDownloadHandler)handler progress: (CXDownloadProgressHandler)progress;@end@implementation CXDownloadManager{    CXDownloadProgressHandler _progress;}- (void)downloadWithURL: (NSString *)URL parameters: (NSDictionary *)parameters handler: (CXDownloadHandler)handler progress: (CXDownloadProgressHandler)progress{    //建立請求對象    NSURLRequest * request = [self postRequestWithURL: URL params: parameters];     NSURLSession * session = [NSURLSession sharedSession];    //執行請求任務    NSURLSessionDataTask * task = [session dataTaskWithRequest: request completionHandler: ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {        if (handler) {            dispatch_async(dispatch_get_main_queue(), ^{                handler(data, error);            });         }    }];    [task resume];}//進度協議方法- (void)URLSession:(NSURLSession *)session     downloadTask:(NSURLSessionDownloadTask *)downloadTask     didWriteData:(int64_t)bytesWritten // 每次寫入的data位元組數     totalBytesWritten:(int64_t)totalBytesWritten // 當前一共寫入的data位元組數    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite // 期望收到的所有data位元組數  {       double downloadProgress = totalBytesWritten / (double)totalBytesExpectedToWrite;      if (_progress) { _progress(downloadProgress); }}  @end

上面通過封裝NSURLSession的請求,傳入一個處理請求結果的block對象,就會自動將請求任務放到背景工作執行緒中執行實現,我們在網路請求邏輯的代碼中調用如下:

#define QQMUSICURL @"https://www.baidu.com/link?url=UTiLwaXdh_-UZG31tkXPU62Jtsg2mSbZgSPSR3ME3YwOBSe97Hw6U6DNceQ2Ln1vXnb2krx0ezIuziBIuL4fWNi3dZ02t2NdN6946XwN0-a&wd=&eqid=ce6864b50004af120000000656fe235f"[[CXDownloadManager alloc] downloadWithURL: QQMUSICURL parameters: nil handler ^(NSData * receiveData, NSError * error) {    if (error) { NSLog(@"下載失敗:%@", error) }    else {        //處理下載資料    }} progress: ^(CGFloat progress) {    NSLog(@"下載進度%lu%%", progress*100);}];
總結

block捕獲變數、代碼傳遞、代碼內聯等特性賦予了它多於代理機制的功能和靈活性,儘管它也存在循環參考、不易調試追溯等缺陷,但無可置疑它的優點深受碼農們的喜愛。如何更加靈活的使用block需要我們對它不斷的使用、探究瞭解才能完成

iOS block從零開始

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.