Block 我所理解的回撥

testHs發表於2015-05-30

廢話

開發iOS距離一年還有四個月。block的文章看了很多。也在專案中使用了,但是使用率很低,僅限於最最簡單的介面傳值。

我最近處於一個深度學習和補作業做實驗的階段(重點是補作業和做實驗),而且經歷了兩個專案現在對於OC的理解又更進了一步。於是抽時間把之前下載的關於block的視訊又翻出來看了一下。有一些感受寫出來,以便CM和PLUSUB以後的iOSer可以很快的學習到關於block的使用。以後這樣的文章都會在標題加一個FOR CM AND PLUSUB。

概述

block就是閉包,可以用來回撥。

再簡單一點,指向函式的指標。

我現在只會簡單的使用block,下午寫了個demo利用block封裝了afnetworking然後在另一個介面呼叫afnetworking的請求。當然不復雜。

上程式碼吧,不然再廢話都解釋不清楚什麼是block。

Part1 block的簡單定義

我現在定義一個簡單的block。

void (^myBlock)(NSString *str);//1
myBlock = ^(NSString *str)
{
    NSLog(@"---%@", str);//2
};
myBlock(@"11111");//3
// Outputs:---11111

可以這麼理解,我定義了一個返回值為void型別的block。void後面跟了一個函式,但是這個函式函式名是以^開頭的,需要用括號包起來,引數也需要用括號包起來。引數可以是一個int型別,可以是一個NSString型別,還可以是一個函式!可以是多個引數還可以是一個函式。當然如果是int型別的block就需要return一個返回值了。

然後我們開始對這個void型別的函式進行定義。就是令它等於一個“^+引數”然後裡面像定義一個函式一樣就行了。
在呼叫這個block的時候就直接就myBlock加引數就可以了。

我們可以打斷點來看一下整個block的執行順序。我在程式碼後面加了標示。

執行順序是1 3 2,可以看到myBlock沒有按照程式執行的順序來呼叫而是在呼叫之後執行了2的程式碼。這個例子可能不那麼明顯,但是可以開始初步理解回撥。

Part2 傳參block

下面把難度加大一點,我們把block當引數傳。然後看一個block的例子。
首先來回顧一個c語言裡偉大的typedef,我們開頭會利用typedef定義一個block。如果不利用typedef定義一個block,那麼函式傳參的時候會寫很多程式碼。一會我會詳述。

#import "ViewController.h"
typedef void (^MyBlock)(NSString *str);//typedef定義一個block

@interface ViewController ()
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    MyBlock tblock = ^(NSString *str) {
    NSLog(@"-----%@", str);//1
    };
    [self testBlock:tblock];//2
}
- (void)testBlock:(MyBlock)mBlock
//如果開始沒有用typedef定義的話,此處的函式變為- (void)testBlock:(void(^)(NSString *str))myblock
//簡單的說格式就是 “返回值 + (^) + 引數 + 名字”
{
    mBlock(@"1111");//3
}
// Outputs:-----1111

在上面的程式碼中,我們寫了一個testBlock的函式,將我們的tblock作為引數傳到了testBlock函式裡面,然後再testBlock裡用mBlock呼叫了我們所寫的MyBlock這個函式。

打斷點看一下整體的執行流程,2 3 1。這裡可以更加明顯理解“回撥”的含義。

Part3 點選button觸發block

經歷了上面兩個part基本上應該對block有個簡單的理解。下面再加一點難度,用按鈕觸發一個回撥來改變按鈕的顏色。

不多解釋,直接上程式碼。

#import "ViewController.h"
typedef void (^MyBlock)(UIColor *color);
@interface ViewController ()
@property (nonatomic, strong) UIButton *testbtn;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.testbtn = [UIButton buttonWithType:UIButtonTypeSystem];
    self.testbtn.frame = CGRectMake(0, 0, 200, 40);
    self.testbtn.backgroundColor = [UIColor greenColor];
    [self.view addSubview:self.testbtn];
    [self.testbtn addTarget:self action:@selector(testfunc) forControlEvents:UIControlEventTouchUpInside];
}
- (void)testfunc
{
    MyBlock myblock = ^(UIColor *color) {
    self.testbtn.backgroundColor = color;
    };
    [self testBlock:myblock];
}
- (void)testBlock:(MyBlock)myBlock
{
    UIColor *color = [UIColor redColor];
    myBlock(color);
}

Part4 利用回撥分離afnetwork的請求

直接上程式碼了。

TestNetworkViewController是afnetwork使用的地方。
TestViewController是我們需要呼叫網路請求的地方。
SearchModel是一個model,我用來解析資料的。

//  TestNetworkViewController.h
#import <UIKit/UIKit.h>
#import "SearchModel.h"
typedef void (^GetModel)(SearchModel *model);
@interface TestNetworkViewController : UIViewController
+ (void)getUrl:(GetModel)getmodel;
@end

//TestNetworkViewController.m
#import "TestNetworkViewController.h"
@interface TestNetworkViewController ()
@end
@implementation TestNetworkViewController
- (void)viewDidLoad {
    [super viewDidLoad];
}
+ (void)getUrl:(GetModel)getmodel
{
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager GET:@"http://xxx.xx.xxx.xx:xxxx/app/search_haosou" parameters:@{@"keyword":@"android"} success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
    SearchModel *service = [MTLJSONAdapter modelOfClass:SearchModel.class fromJSONDictionary:responseObject error:nil];
    getmodel(service);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
    }];
}
@end

//  TestViewController.h
#import <UIKit/UIKit.h>
#import "SearchModel.h"
@interface TestViewController : UIViewController
@end

#import "TestViewController.h"
#import "TestNetworkViewController.h"
#import "SearchModel.h"
@interface TestViewController ()
@property (nonatomic, strong) SearchModel *test;
@end

@implementation TestViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    UIButton *testBtn = [UIButton buttonWithType:UIButtonTypeSystem];
    [testBtn setTitle:@"test" forState:UIControlStateNormal];
    [testBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    testBtn.frame = CGRectMake(0, 100, 200, 40);
    [testBtn addTarget:self action:@selector(getUrl) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:testBtn];
    self.view.backgroundColor = [UIColor whiteColor];
}
- (void)getUrl
{
    [TestNetworkViewController getUrl:^(SearchModel *model) {
    self.test = model;
    NSLog(@"



%@", self.test);
    }];
}

總結

首先還是得說一下我這裡只是演示了最最簡單的block使用,因為作為一個iOS開發者不會block,那麼有一大半的程式碼是看不懂的,在後面理解RAC的時候也會出現很大的問題。所以這裡我只是做了簡單的說明,至於在block裡為什麼不能呼叫self,自從segmentfault上有人問過之後我答錯之後現在也算是理解了一部分了,但是牽扯到了記憶體回收的問題,我還需要一段時間來消化。消化完之後會繼續寫部落格的。

總之,block非常的有用,初期寫block非常的難過,但是難過過了也就慢慢會喜歡上block的。

相關文章