Objective-C 中的 Block

作者: f94bd4cac294 | 来源:发表于2016-08-20 17:28 被阅读282次

    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需要多次调用, 会有各种循环引用的问题.

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

    相关文章

      网友评论

        本文标题:Objective-C 中的 Block

        本文链接:https://www.haomeiwen.com/subject/rfdisttx.html