Objective-C 中的 Block

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通过点击导航按钮返回, 把secondViewControllertitle赋值给viewControllerlabel. 这是很常见的从后往前传值, 一般遇到这种情况, 我们经常都使用协议传值, 而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需要多次调用, 会有各种循环引用的问题.

如有不足之处, 还望各位指出

    原文作者:f94bd4cac294
    原文地址: https://www.jianshu.com/p/759e7fca8a60
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞