Block 我所理解的回調

廢話

開發iOS距離一年還有四個月。block的文章看了不少。也在項目中使用了,可是使用率很低,僅限於最最簡單的界面傳值。android

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

概述

block就是閉包,能夠用來回調。網絡

再簡單一點,指向函數的指針。閉包

我如今只會簡單的使用block,下午寫了個demo利用block封裝了afnetworking而後在另外一個界面調用afnetworking的請求。固然不復雜。app

上代碼吧,否則再廢話都解釋不清楚什麼是block。ide

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加參數就能夠了。atom

咱們能夠打斷點來看一下整個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(@"\n\n\n\n%@", self.test);
    }];
}

總結

首先仍是得說一下我這裏只是演示了最最簡單的block使用,由於做爲一個iOS開發者不會block,那麼有一大半的代碼是看不懂的,在後面理解RAC的時候也會出現很大的問題。因此這裏我只是作了簡單的說明,至於在block裏爲何不能調用self,自從segmentfault上有人問過以後我答錯以後如今也算是理解了一部分了,可是牽扯到了內存回收的問題,我還須要一段時間來消化。消化完以後會繼續寫博客的。

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

相關文章
相關標籤/搜索