1.Block的聲明: 返回值類型(^block名稱)(參數)編程
例如:聲明一個無參數無返回值的block:app
// 聲明:返回值類型(^block變量名)(參數) void(^block)();框架
2.Block的定義:atom
void(^block1)() = ^(){ spa
NSLog(@"調用block1"); 3d
};代理
void(^block2)() = ^{ };
int(^block3)() = ^int{//int能夠省略 return 2; };
3.Block的做用:保存一段代碼,在調用Block的時候執行.指針
4.Block的調用:block名(參數);code
// 調用Block,就會去查看Block所保存代碼 block1();
5.Xcode快捷生成block的方式:inlineBlock對象
<#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) { <#statements#> };
6.Block起別名:
// BlockType就是類型別名 typedef void(^BlockType)();
7.Block定義屬性:
// Block怎麼聲明.就怎麼定義屬性 // block:屬性名 @property (nonatomic ,strong) void(^block)();
例:新建一個CellItem做爲TableViewController的模型
#import <Foundation/Foundation.h> @interface CellItem : NSObject @property (nonatomic ,strong) void(^block)(); @property (nonatomic ,strong) NSString *title; @end @implementation CellItem @end
#import <UIKit/UIKit.h> @interface TableViewController : UITableViewController @end #import "CellItem.h" // tableView:打電話,發短信,發郵件 @interface TableViewController () @property (nonatomic ,strong) NSArray *cellArr; @end @implementation TableViewController - (void)viewDidLoad { [super viewDidLoad]; // 打電話 CellItem *item = [[CellItem alloc] init]; item.title = @"打電話"; item.block = ^{ NSLog(@"打電話"); }; // 發短信 CellItem *item1 = [[CellItem alloc] init]; item1.title = @"發短信"; item1.block = ^{ NSLog(@"發短信"); }; // 發郵件 CellItem *item2 = [[CellItem alloc] init]; item2.title = @"發郵件"; item2.block = ^{ NSLog(@"發郵件"); }; self.cellArr = @[item,item1,item2]; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.cellArr.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *ID = @"cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } cell.textLabel.text = [self.cellArr[indexPath.row] title]; return cell; } // 點擊cell就會調用 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { CellItem *item = self.cellArr[indexPath.row]; if (item.block) { item.block(); } } @end
數據傳值分順傳和逆傳.
假設:A控制器push(modal)到B控制器,若A要給B傳值,則此種傳值方式爲順傳;B給A傳值則爲逆傳.
開發中,你們比較熟悉的方法:順傳--->定義屬性;逆傳--->使用代理.
下文介紹,使用Block替換代理實現數據逆傳.
#import <UIKit/UIKit.h> @class ModalViewController; @protocol ModalViewControllerDelegate <NSObject> @optional // 代理方法:想要告訴代理作什麼事情 - (void)modalViewController:(ModalViewController *)modalVc receiveStr:(NSString *)str; @end @interface ModalViewController : UIViewController @property (nonatomic, weak) id<ModalViewControllerDelegate> delegate; @end @implementation ModalViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 傳值給ViewController if ([self.delegate respondsToSelector:@selector(modalViewController:receiveStr:)]) { [self.delegate modalViewController:self receiveStr:@"123"]; } } @end
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end #import "ModalViewController.h" @interface ViewController ()<ModalViewControllerDelegate> @end @implementation ViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { ModalViewController *modalVc = [[ModalViewController alloc] init]; modalVc.delegate = self; modalVc.view.backgroundColor = [UIColor yellowColor]; [self presentViewController:modalVc animated:YES completion:nil]; } #pragma mark -ModalViewControllerDelegate - (void)modalViewController:(ModalViewController *)modalVc receiveStr:(NSString *)str { NSLog(@"接收到值%@",str); } @end
#import <UIKit/UIKit.h> @interface ModalViewController : UIViewController @property (nonatomic ,strong) void(^valueBlock)(NSString *value); @end @implementation ModalViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (self.valueBlock) { self.valueBlock(@"123"); } } @end
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end #import "ModalViewController.h" @implementation ViewController - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { ModalViewController *modalVc = [[ModalViewController alloc] init]; modalVc.valueBlock = ^(NSString *value){ NSLog(@"接收到%@",value); }; modalVc.view.backgroundColor = [UIColor yellowColor]; [self presentViewController:modalVc animated:YES completion:nil]; } @end
首先須要搞清楚一個問題:block是對象嗎?
蘋果官方文檔告訴你,沒錯,她就是對象.這樣咱們能夠經過%@來打印她.
1.在非ARC中:
@interface ViewController () @property (nonatomic, copy) void(^block)(); @end - (void)viewDidLoad { [super viewDidLoad]; int a = 0; void(^block)() = ^{ NSLog(@"調用block"); NSLog(@"%d",a); }; self.block = block; NSLog(@"block - %@",block);//block - <__NSStackBlock__: 0x7fff5b9fa9c8> [棧] NSLog(@"self.block - %@",self.block);// self.block - <__NSMallocBlock__: 0x7fd2e0418310> [堆] }
@interface ViewController () @property (nonatomic, copy) void(^block)(); @end - (void)viewDidLoad { [super viewDidLoad]; int a = 0; void(^block)() = ^{ NSLog(@"調用block"); //NSLog(@"%d",a); }; self.block = block; NSLog(@"block - %@",block); //block - <__NSGlobalBlock__: 0x105d01080> NSLog(@"self.block - %@",self.block);//self.block - <__NSGlobalBlock__: 0x105d01080> }
@interface ViewController () @property (nonatomic, copy) void(^block)(); @end static NSString *str = @"iOS"; - (void)viewDidLoad { [super viewDidLoad]; int a = 0; void(^block)() = ^{ NSLog(@"調用block"); //NSLog(@"%d",a); NSLog(@"%@",str); }; self.block = block; NSLog(@"block - %@",block); //block - <__NSGlobalBlock__: 0x100b8e080> NSLog(@"self.block - %@",self.block); //block - <__NSGlobalBlock__: 0x100b8e080> }
注意: 在非ARC中.不能使用retain引用block,由於block訪問外部局部變量時,retain不會把block放在堆裏面(仍然存放在棧裏),生命週期沒有獲得延長.
在非ARC中使用copy,纔會把block放在堆裏面.
@interface ViewController () //@property (nonatomic, copy) void(^block)(); @property (nonatomic, retain) void(^block)(); @end - (void)viewDidLoad { [super viewDidLoad]; int a = 0; void(^block)() = ^{ NSLog(@"調用block"); NSLog(@"%d",a); //NSLog(@"%@",str); }; self.block = block; NSLog(@"block - %@",block);// block - <__NSStackBlock__: 0x7fff55d5b9c8> NSLog(@"self.block - %@",self.block);//self.block - <__NSStackBlock__: 0x7fff55d5b9c8> }
2. ARC環境:
可使用strong去引用
@interface ViewController () @property (nonatomic, strong) void(^block)(); @end - (void)viewDidLoad { [super viewDidLoad]; int a = 0; // ARC中,默認局部對象變量都是強指針 void(^block)() = ^{ NSLog(@"調用block"); NSLog(@"%d",a); }; self.block = block; NSLog(@"%@",block);//<__NSMallocBlock__: 0x7f9218c3b810> NSLog(@"%@",self.block);// <__NSMallocBlock__: 0x7f86fa6133d0> }
概括:只要block訪問的變量是整個app運行過程當中都存在的變量,那麼block確定存放在全局區.
Block循環引用,跟block調用沒有關係
block只要訪問外部強指針對象變量,就會對這個變量進行強引用.
1 #import <UIKit/UIKit.h> 2 3 @interface ModalViewController : UIViewController 4 5 @end 6 7 @interface ModalViewController () 8 9 @property (nonatomic ,strong) void(^block)(); 10 11 @end 12 13 @implementation ModalViewController 14 15 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 16 { 17 [self dismissViewControllerAnimated:YES completion:nil]; 18 } 19 20 - (void)dealloc 21 { 22 NSLog(@"%@對象銷燬",self); 23 } 24 25 - (void)viewDidLoad { 26 [super viewDidLoad]; 27 28 // Block循環引用,跟block調用沒有關係 29 // block只要訪問外部強指針對象變量,就會對這個變量進行強引用. 30 31 self.block = ^{ 32 NSLog(@"延遲打印%@",self); 33 }; 34 35 self.block(); 36 } 37 @end
1 #import <UIKit/UIKit.h> 2 3 @interface ViewController : UIViewController 4 5 @end 6 7 #import "ModalViewController.h" 8 9 @implementation ViewController 10 11 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 12 { 13 ModalViewController *modalVc = [[ModalViewController alloc] init]; 14 15 modalVc.view.backgroundColor = [UIColor redColor]; 16 17 //self.presentedViewController強引用modalVc 18 [self presentViewController:modalVc animated:YES completion:nil]; 19 } 20 @end
點擊viewController的屏幕modal出來ModalViewController,然而再點擊ModalViewController的屏幕會發現ModalViewController並無被銷燬.
緣由:發生了循環引用.block只要訪問外部強指針對象變量(self==ModalViewController),就會對這個變量進行強引用.而ModalViewController對block也進行着強引用.
解決辦法:使用__weak修飾符.讓block對modalVc進行弱引用.
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 // Block循環引用,跟block調用沒有關係 5 // block只要訪問外部強指針對象變量,就會對這個變量進行強引用. 6 __weak typeof(self) weakSelf = self; 7 8 self.block = ^{ 9 10 NSLog(@"延遲打印%@",weakSelf); 11 12 }; 13 14 //self.block(); 15 } 16 @end
需求:在block中處理延時操做,須要訪問modalVc即便立刻dismiss控制器,保證仍能訪問.即實現dismiss延遲銷燬控制器.
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 // Block循環引用,跟block調用沒有關係 5 // block只要訪問外部強指針對象變量,就會對這個變量進行強引用. 6 __weak typeof(self) weakSelf = self; 7 8 self.block = ^{ 9 10 __strong typeof(weakSelf) strongSelf = weakSelf; 11 12 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 13 // 引用的爲內部強指針 14 NSLog(@"延遲打印%@",strongSelf); 15 16 }); 17 18 }; 19 20 self.block(); 21 }
2016-04-10 20:08:19.918 06-Block循環引用[7884:212378] 延遲打印<ModalViewController: 0x7f9a31e1ebe0>
2016-04-10 20:08:19.936 06-Block循環引用[7884:212378] <ModalViewController: 0x7f9a31e1ebe0>對象銷燬
self.block();執行完畢而且dismiss,仍然有系統的強指針指向after那個block塊,保證modalVc不被當即銷燬.當延時block快執行完畢,strongSelf指針再也不強引用modalVc,此時modalVc控制器對象才能銷燬.
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 // 若是局部變量被static,__block修飾,那麼都是指針傳遞 5 // 全局變量.也是指針傳遞 6 __block int a = 5; 7 8 // 默認局部變量 在block中 是 值傳遞 9 void(^block)() = ^{ 10 11 NSLog(@"%d",a);//10 12 }; 13 14 a = 10; 15 16 block(); 17 }
1 int a = 5; 2 3 @implementation ViewController 4 5 - (void)viewDidLoad { 6 [super viewDidLoad]; 7 8 // 若是局部變量被static,__block修飾,那麼都是指針傳遞 9 // 全局變量.也是指針傳遞 10 //__block int a = 5; 11 12 // 默認局部變量 在block中 是 值傳遞 13 void(^block)() = ^{ 14 15 NSLog(@"%d",a);//10 16 }; 17 18 a = 10; 19 20 block(); 21 } 22 23 @end
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 // 若是局部變量被static,__block修飾,那麼都是指針傳遞 5 // 全局變量.也是指針傳遞 6 int a = 5; 7 8 // 默認局部變量 在block中 是 值傳遞 9 void(^block)() = ^{ 10 11 NSLog(@"%d",a);//5 12 }; 13 14 a = 10; 15 16 block(); 17 }
其實Block做爲方法的參數使用,咱們開發中常用.
例如:
[UIView animateWithDuration:1 animations:^{ }];
只要方法參數帶有^,就表示把block當作參數去使用.
以上的例子中,把block當作參數去使用,block並非咱們調用,而是系統調用.
使用場景: 當本身封裝一個類的時候,有些事情好比怎麼作由外部決定,可是何時去作,由內部決定,這時候採起使用block.
需求:封裝一個計算器,提供一個計算方法,怎麼計算由外界去決定,何時計算,本身管理.
1 #import <Foundation/Foundation.h> 2 3 @interface CalculateManager : NSObject 4 5 /** 保存計算的結果 */ 6 @property (nonatomic, assign) int result; 7 8 /** 9 * 計算 10 * 11 * @param block 交待怎麼計算的block 12 */ 13 - (void)calculate:(int(^)(int))block; 14 15 @end 16 17 @implementation CalculateManager 18 19 - (void)calculate:(int (^)(int))block 20 { 21 self.result = block(self.result); 22 } 23 @end
1 #import <UIKit/UIKit.h> 2 3 @interface ViewController : UIViewController 4 5 @end 6 7 8 #import "CalculateManager.h" 9 10 @interface ViewController () 11 12 @end 13 14 @implementation ViewController 15 16 - (void)viewDidLoad { 17 [super viewDidLoad]; 18 19 // 建立計算器管理者 20 CalculateManager *mgr = [[CalculateManager alloc] init]; 21 22 [mgr calculate:^(int result){ 23 // 計算 24 result += 5; 25 result *= 2; 26 27 return result; 28 }]; 29 30 NSLog(@"%d",mgr.result); 31 32 } 33 @end
其實Block做爲方法的返回值使用,咱們開發中可能不少人都用過.
例如:目前最流行的AutoLayout框架Masonry
[targetView mas_makeConstraints:^(MASConstraintMaker *make) { make.bottom.equalTo(self.view).offset(-20); make.leading.equalTo(self.view).offset(20); make.size.mas_equalTo(CGSizeMake(100, 100)); }];
通常調用方法後面接(),就是把block當作返回值去用.
編程思想:
鏈式編程:把方法的調用 經過.語法 連接起來,可讀性很是好.
#import <Foundation/Foundation.h> @interface CalculateManager : NSObject /** 保存計算的結果 */ @property (nonatomic, assign) int result; /** * 加一個值返回一個計算管理對象 */ - (CalculateManager *)add:(int)value; /** * 返回一個 帶有整形參數返回值是計算管理對象的 block */ - (CalculateManager *(^)(int))add; @end @implementation CalculateManager - (CalculateManager *)add:(int)value { self.result += value; return self; } - (CalculateManager *(^)(int))add { return ^(int value){ self.result += value; return self; }; } @end
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end #import "CalculateManager.h" /* 需求:封裝計算器類,提供加號方法,用於處理加法計算. */ @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; CalculateManager *mgr = [[CalculateManager alloc] init]; [[[[[mgr add:5] add:5] add:5] add:5] add:5]; mgr.add(5).add(5).add(5).add(5).add(5); NSLog(@"%d",mgr.result);//50 }