Block是一種蘋果開發的基于C的調用方式, 從iOS 4.0引入之后, 似乎就受到了Apple的特殊照顧和開發者的喜愛. 在如今的開發中, Block雖然有不足的地方, 但也依然被廣泛的使用. 從字面意思來看, Block就是塊, 也就是有某種功能的代碼段. 本文主要介紹的Block的基本用法, 同時談談Block與Delegation各自的優劣.
一.Block基本語法
BOOL (^isInputEven)(int) = ^(int input) {
if (input % 2 == 0) {
return YES;
} else {
return NO;
}
};
這是一個很簡單的Block, 對比C語言的函數是不是感覺很相似, BOOL
為這個Block的返回值, ^后的isInputEven
為Block的函數名, int
為該block接受的參數類型, =后面的int intPut
是對這個參數的描述, 在這個block中input
用來指代傳入的參數. 剛開始使用Block時, 應該都會為這個語法頭疼.但是習慣之后發現其實就是平時我們用的方法的另一種寫法.
- 想用使用這個Block也很簡單, 就如C語言函數.
isInputEven(5);
NSLog(@"%@", isInputEven(5) ? @"is Even" : @"is not even");
- Block的幾種形式
// 有參有返回值
int (^sum)(int, int) = ^(int a, int b) {
return a + b;
};
// 無參無返回
void (^noParameterOrReturnValue)(void) = ^(void) {
};
// 無參無返回也可直接寫為
void (^block)() = ^{
};
// 有參無返回值
void (^handleNumber)(int number) = ^(int number) {
};
// 無參有返回
NSString *(^returnString)() = ^ {
return @"無參有返回值";
};
二.Block的使用
- block作為屬性使用
在viewController
中push到SecondViewController
, 第二個VC通過點擊導航按鈕返回, 把secondViewController
的title
賦值給viewController
的label
. 這是很常見的從后往前傳值, 一般遇到這種情況, 我們經常都使用協議傳值, 而Block的使用就比Delegation方便了很多.
首先在SecondViewController.h中聲明Block屬性, 可以把 void(^)(NSString *)
看作類型, secondVCTitle
則為屬性名.
@interface SecondViewController : UIViewController
@property (nonatomic, copy) void (^secondVCTitle)(NSString *title);
@end
SecondViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"Second";
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(backToVC:)];
}
- (void)backToVC:(UIBarButtonItem *)barButtonItem {
// secondViewController返回之前設置block要傳的值
self.secondVCTitle(self.title);
[self.navigationController popViewControllerAnimated:YES];
}
viewController中button的點擊方法
- (IBAction)pushToSecondVC:(id)sender {
SecondViewController *secondVC = [[SecondViewController alloc] init];
secondVC.secondVCTitle = ^(NSString *title) {
// 接收block傳過來的值
_titleLabel.text = title;
};
[self.navigationController pushViewController:secondVC animated:YES];
}
這樣很簡單的幾步就把后一個VC的值傳了過來, 是不是比Delegation簡單了很多.
- block作為方法參數使用
下面以一個自定義view為例
#import <UIKit/UIKit.h>
@interface CusView : UIView
// block作為方法參數
- (void)playButton:(void (^)(UIButton *play))playButton;
@end
cusView
中只創建了一個button控件, 在.m中實現playButton:方法, 需要一個block屬性
#import "CusView.h"
@interface CusView ()
@property (nonatomic, strong) UIButton *playButton;
// 帶一個參數的block屬性
@property (nonatomic, copy) void (^playBut)(UIButton * play);
@end
@implementation CusView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_playButton = [UIButton buttonWithType:UIButtonTypeCustom];
_playButton.backgroundColor = [UIColor yellowColor];
[_playButton addTarget:self action:@selector(playButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_playButton];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
_playButton.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
}
// 帶block參數的方法
- (void)playButton:(void (^)(UIButton *))playButton {
self.playBut = playButton;
}
- (void)playButtonClicked:(UIButton *)playButton {
self.playBut(playButton);
}
@end
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 創建cusView
CusView *cusView = [[CusView alloc] initWithFrame:CGRectMake(0, 64, 50, 50)];
[self.view addSubview:cusView];
// 調用playButton方法
[cusView playButton:^(UIButton *play) {
NSLog(@"點擊了playButton");
}];
}
三. Block相關的修飾符
- __block
- __weak
- __strong
__block
- 當我們想要在block中修改a的值, 估計會這樣寫, 但實際上block只能訪問局部變量, 得到的只是該變量的副本, 修改之后也不會影響原來的值.
// wrong
int a = 0;
void (^blockTest)() = ^{
a = 100;
};
- 想要修改a的值 則需要加上__block修飾
__block int a = 0;
void (^blockTest)() = ^{
a = 100;
};
- __block在MRC環境下還有一個作用, 能防止block對內部的對象進行強引用, 也就是防止循環引用.
__weak
__weak弱引用, 用__weak修飾變量, 當變量消失時, 會自動把對象置空, 可以防止循環引用(只作用在ARC環境).
__strong
__strong強引用:strong和retain相似,只要有一個strong指針指向對象,該對象就不會被銷毀. 在ARC環境下, 雖然沒有顯示的聲明,但是Objective-C默認聲明的一個對象就為 __strong.
// 兩者等價
id object = [[NSObject alloc] init];
id __strong object = [[NSObject alloc] init];
四.Block與Delegation
Delegation的優點: 通常被weak引用, 不會出現內存泄漏問題, 可以將一類功能的方法結合在一起.需要在兩個界面間傳遞的信息比較多時, 使用起來比block更好.
缺點: 應該是代碼比較多, 比較麻煩.Block的優點: 簡化代碼,增強代碼可讀性, 不需要代理人來傳遞, 可以用作參數傳遞.
缺點: 如果block需要多次調用, 會有各種循環引用的問題.
如有不足之處, 還望各位指出