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需要多次調用, 會有各種循環引用的問題.

如有不足之處, 還望各位指出

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Apple從OS X 10.4和iOS 4以后開始支持block,相對于delegate,block有很多便捷之處...
    HK_Hank閱讀 12,586評論 1 46
  • 原文地址:Objective-C中的Block 1.相關概念 在這篇筆記開始之前,我們需要對以下概念有所了解。 1...
    默默_David閱讀 422評論 0 1
  • .相關概念 在這篇筆記開始之前,我們需要對以下概念有所了解。 1.1 操作系統中的棧和堆 注:這里所說的堆和棧與數...
    狼鳳皇閱讀 490評論 0 0
  • 1.相關概念 在這篇筆記開始之前,我們需要對以下概念有所了解。 1.1 操作系統中的棧和堆 注:這里所說的堆和棧與...
    DevTalking閱讀 3,745評論 3 76
  • 演繹法 第二天,我們依他的安排碰面,一起到貝克街221B號看房。臥房舒適宜人,會客室寬敞通風。裝修風格明快,兩大扇...
    史黛拉945閱讀 428評論 0 1