開發iOS距離一年還有四個月。block的文章看了不少。也在項目中使用了,可是使用率很低,僅限於最最簡單的界面傳值。android
我最近處於一個深度學習和補做業作實驗的階段(重點是補做業和作實驗),並且經歷了兩個項目如今對於OC的理解又更進了一步。因而抽時間把以前下載的關於block的視頻又翻出來看了一下。有一些感覺寫出來,以便CM和PLUSUB之後的iOSer能夠很快的學習到關於block的使用。之後這樣的文章都會在標題加一個FOR CM AND PLUSUB。segmentfault
block就是閉包,能夠用來回調。網絡
再簡單一點,指向函數的指針。閉包
我如今只會簡單的使用block,下午寫了個demo利用block封裝了afnetworking而後在另外一個界面調用afnetworking的請求。固然不復雜。app
上代碼吧,否則再廢話都解釋不清楚什麼是block。ide
我如今定義一個簡單的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的代碼。這個例子可能不那麼明顯,可是能夠開始初步理解回調。
下面把難度加大一點,咱們把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。這裏能夠更加明顯理解「回調」的含義。
經歷了上面兩個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); }
直接上代碼了。
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的。