Block是一種蘋果開發(fā)的基于C的調(diào)用方式, 從iOS 4.0引入之后, 似乎就受到了Apple的特殊照顧和開發(fā)者的喜愛. 在如今的開發(fā)中, Block雖然有不足的地方, 但也依然被廣泛的使用. 從字面意思來看, Block就是塊, 也就是有某種功能的代碼段. 本文主要介紹的Block的基本用法, 同時(shí)談?wù)凚lock與Delegation各自的優(yōu)劣.
一.Block基本語法
BOOL (^isInputEven)(int) = ^(int input) {
if (input % 2 == 0) {
return YES;
} else {
return NO;
}
};
這是一個(gè)很簡(jiǎn)單的Block, 對(duì)比C語言的函數(shù)是不是感覺很相似, BOOL
為這個(gè)Block的返回值, ^后的isInputEven
為Block的函數(shù)名, int
為該block接受的參數(shù)類型, =后面的int intPut
是對(duì)這個(gè)參數(shù)的描述, 在這個(gè)block中input
用來指代傳入的參數(shù). 剛開始使用Block時(shí), 應(yīng)該都會(huì)為這個(gè)語法頭疼.但是習(xí)慣之后發(fā)現(xiàn)其實(shí)就是平時(shí)我們用的方法的另一種寫法.
- 想用使用這個(gè)Block也很簡(jiǎn)單, 就如C語言函數(shù).
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
, 第二個(gè)VC通過點(diǎn)擊導(dǎo)航按鈕返回, 把secondViewController
的title
賦值給viewController
的label
. 這是很常見的從后往前傳值, 一般遇到這種情況, 我們經(jīng)常都使用協(xié)議傳值, 而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返回之前設(shè)置block要傳的值
self.secondVCTitle(self.title);
[self.navigationController popViewControllerAnimated:YES];
}
viewController中button的點(diǎn)擊方法
- (IBAction)pushToSecondVC:(id)sender {
SecondViewController *secondVC = [[SecondViewController alloc] init];
secondVC.secondVCTitle = ^(NSString *title) {
// 接收block傳過來的值
_titleLabel.text = title;
};
[self.navigationController pushViewController:secondVC animated:YES];
}
這樣很簡(jiǎn)單的幾步就把后一個(gè)VC的值傳了過來, 是不是比Delegation簡(jiǎn)單了很多.
- block作為方法參數(shù)使用
下面以一個(gè)自定義view為例
#import <UIKit/UIKit.h>
@interface CusView : UIView
// block作為方法參數(shù)
- (void)playButton:(void (^)(UIButton *play))playButton;
@end
cusView
中只創(chuàng)建了一個(gè)button控件, 在.m中實(shí)現(xiàn)playButton:方法, 需要一個(gè)block屬性
#import "CusView.h"
@interface CusView ()
@property (nonatomic, strong) UIButton *playButton;
// 帶一個(gè)參數(shù)的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參數(shù)的方法
- (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.
// 創(chuàng)建cusView
CusView *cusView = [[CusView alloc] initWithFrame:CGRectMake(0, 64, 50, 50)];
[self.view addSubview:cusView];
// 調(diào)用playButton方法
[cusView playButton:^(UIButton *play) {
NSLog(@"點(diǎn)擊了playButton");
}];
}
三. Block相關(guān)的修飾符
- __block
- __weak
- __strong
__block
- 當(dāng)我們想要在block中修改a的值, 估計(jì)會(huì)這樣寫, 但實(shí)際上block只能訪問局部變量, 得到的只是該變量的副本, 修改之后也不會(huì)影響原來的值.
// wrong
int a = 0;
void (^blockTest)() = ^{
a = 100;
};
- 想要修改a的值 則需要加上__block修飾
__block int a = 0;
void (^blockTest)() = ^{
a = 100;
};
- __block在MRC環(huán)境下還有一個(gè)作用, 能防止block對(duì)內(nèi)部的對(duì)象進(jìn)行強(qiáng)引用, 也就是防止循環(huán)引用.
__weak
__weak弱引用, 用__weak修飾變量, 當(dāng)變量消失時(shí), 會(huì)自動(dòng)把對(duì)象置空, 可以防止循環(huán)引用(只作用在ARC環(huán)境).
__strong
__strong強(qiáng)引用:strong和retain相似,只要有一個(gè)strong指針指向?qū)ο螅搶?duì)象就不會(huì)被銷毀. 在ARC環(huán)境下, 雖然沒有顯示的聲明嘿棘,但是Objective-C默認(rèn)聲明的一個(gè)對(duì)象就為 __strong.
// 兩者等價(jià)
id object = [[NSObject alloc] init];
id __strong object = [[NSObject alloc] init];
四.Block與Delegation
Delegation的優(yōu)點(diǎn): 通常被weak引用, 不會(huì)出現(xiàn)內(nèi)存泄漏問題, 可以將一類功能的方法結(jié)合在一起.需要在兩個(gè)界面間傳遞的信息比較多時(shí), 使用起來比block更好.
缺點(diǎn): 應(yīng)該是代碼比較多, 比較麻煩.Block的優(yōu)點(diǎn): 簡(jiǎn)化代碼,增強(qiáng)代碼可讀性, 不需要代理人來傳遞, 可以用作參數(shù)傳遞.
缺點(diǎn): 如果block需要多次調(diào)用, 會(huì)有各種循環(huán)引用的問題.
如有不足之處, 還望各位指出