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需要多次调用, 会有各种循环引用的问题.
如有不足之处, 还望各位指出