iOS - Block的写法及使用

简介: Block技术合集iOS - Block变量截获

1. Block定义


//声明定义
    int(^ sumOfNumbers)(int a, int b) = ^(int a, int b) {
        return a + b;
    };
    int sum = sumOfNumbers(1, 2);
    NSLog(@"sum = %d", sum);
//打印
2021-05-01 11:04:05.920543+0800 BlockDemo[18351:6429226] sum = 3


这段代码等号左侧声明一个名为sumOfNumbers的代码块,名称前用^符号表示后面的字符串是block的名称。最左侧的int表示这个block的返回值,括号中间表示这个block的参数列表,这里接收两个int类型的参数。 而在等号右侧表示这个block的定义,其中返回值是可以省略的,编译器会根据上下文自动补充返回值类型。使用^符号衔接着一个参数列表,使用括号包起来,告诉编译器这是一个block,然后使用大括号将block的代码封装起来。


image.png

Block


2. Block写法


  • return_type 表示返回的对象/关键字等(通常是void)


  • blockName 表示block的名称


  • var_type 表示参数的类型


  • varName 表示参数名称


1. 做为局部变量

return_type (^blockName)(var_type) = ^return_type (var_type varName) {
    // ...
};
blockName(var);
int(^ sumOfNumbers)(int a, int b) = ^(int a, int b) {
  return a + b;
};


2. 做为属性

@property (copy) return_type (^blockName) (var_type);
@property (nonatomic, copy) void(^ didLoginSuccess)(NSString *username);
@property (nonatomic, copy) void(^ didLoginFailed)(void);


3. 定义方法时,做为Block型的形参数

- (void)yourMethod:(return_type (^)(var_type))blockName;
- (void)sumOfA:(int)a B:(int)b sumBlock:(void(^)(int sum))sumBlock {
    int sum = a + b;
    sumBlock(sum);
}
- (void)login:(void(^)(void))completion {
    completion();
}


4. Block作为实参


Block作为参数使用,常见于各框架之中,比如在封装一个类时,当做什么事情由外界去决定,什么时候调用由自己的类决定时,这时候就需要将block作为参数使用。

[someObject doSomethingWithBlock: ^return_type (var_type varName)
{
    //...
}];


以自定义一个简单的工具类CalculatorManager为例:

//
//  CalculatorManger.h
//  BlockDemo
//
//  Created by Ternence on 2021/5/1.
//
#import <Foundation/Foundation.h>
@interface CalculatorManger : NSObject
//计算结果值
@property (nonatomic, assign) int result;
+ (instancetype)sharedManager;
//block作为参数时格式与其它类型定义时一致,都是(类型)变量名,看起来有些晕人
- (void)calculate:(int(^)(int))calculateBlock;
@end

//
//  CalculatorManger.m
//  BlockDemo
//
//  Created by Ternence on 2021/5/1.
//
#import "CalculatorManger.h"
static int defaultHeight = 100;
static CalculatorManger * instance = nil;
@implementation CalculatorManger
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (instance == nil) {
            instance = [super allocWithZone:zone];
        }
    });
    return  instance;;
}
+ (instancetype)sharedManager {
    return [[self alloc] init];
}
//方法中定义了一个block数据类型参数(返回值为int类型的,且带有一个int类型的形参)
- (void)calculate:(int (^)(int))calculateBlock {
    //calculateBlock接受外界传入的代码块,也就意味着怎么去操作是由外界调用者决定的
    _result = calculateBlock(defaultHeight);
}
@end


外界控制器调用

CalculatorManger *manager = [CalculatorManger sharedManager];
    [manager calculate:^int(int i) {
        NSLog(@"=======i : %d", i);
        for (int j = 0; j < 100; j ++) {
            i++;
        }
        return i;
    }];
    NSLog(@"=====result: %d", manager.result);


可以看到,工具类CalculcatorManager的计算方法calculate:^int(int)calculateBlock其具体实现,交由了外界的控制器调用者去决定了。虽然有些许绕,但只要搞清楚block的作为参数使用时的格式,理解起来也很快的,如果先前对这类型的用法在理解上抱有疑惑的话,希望这个小例子能帮到您:)


5. 匿名Block


Block实现时,如上文的局部变量和实参,等号右边就是一个匿名Block,它没有blockName:

^return_type (var_type varName)
{
    //...
};


6. typedef Block


利用typedef简化Block的声明

typedef return_type (^BlockTypeName)(var_type);
typedef void (^didQuitAcountBlock)(void);


使用

BlockTypeName aBlock = ^return_type (var_type) {
    //...
}
didQuitAcountBlock quitBlock = ^void(void) {
  NSLog(@"did quit acount");
};
quitBlock();


7. 内联 Block


这种形式并不常用,匿名Block声明后立即被调用,内联 Block可用于代码分块,提高代码可读性,功能类似大括号的代码块,其它功能非常有限

^return_type (var_type varName)
{
    //...
}(var);
^void() {
        NSLog(@"内联Block声明后即被调用");
 }();


8. 递归调用Block


Block内部调用自身,递归调用是很多算法基础,特别是在无法提前预知循环终止条件的情况下。


注意 由于Block内部引用了自身,这里必须使用__block避免保留环问题。

__block return_type (^blockName)(var_type) = [^return_type (var_type varName)
{
    if (returnCondition)
    {
        blockName = nil;
        return;
    }
    // ...
    blockName(varName);
} copy];
blockName(varValue);


实例

__block int i = 5;
    __block void (^ dismisssController)(void) = [^void(void) {
        if (i == 0) {
            dismisssController = nil;
            return;
        }
        NSLog(@"recursionBlock i = %d", i);
        i --;
        dismisssController();
    } copy];
    dismisssController();


9. Block做为返回值

- (return_type(^)(var_type))methodName
{
    // ...
}


3. 一个Block传参的例子


.h

@interface SecondViewController : UIViewController
@property (nonatomic, copy) void(^ didLoginSuccess)(NSString *username);
@property (nonatomic, copy) void(^ didLoginFailed)(void);
@end

.m

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (self.didLoginSuccess) {
        self.didLoginSuccess(@"码代码的小马");
    }
    if (self.didLoginFailed) {
        self.didLoginFailed();
    }
}


调用block

SecondViewController *vc = [[SecondViewController alloc] init];
    [self.navigationController pushViewController:vc animated:true];
    vc.didLoginSuccess = ^(NSString * _Nonnull username) {
        NSLog(@"congratulations %@! welcome to objc world", username);
    };
    vc.didLoginFailed = ^{
        NSLog(@"login failed");
    };


相关文章
|
iOS开发
iOS block修饰符用copy还是strong
iOS block修饰符用copy还是strong
153 0
|
iOS开发 Python
iOS小技能:lldb打印block参数签名
iOS逆向时经常会遇到参数为block类型,本文介绍一个lldb script,可快速打印出Objective-C方法中block参数的类型。
200 0
iOS小技能:lldb打印block参数签名
|
iOS开发 开发者
iOS开发 - 如何写出漂亮的block
iOS开发 - 如何写出漂亮的block
105 0
|
iOS开发
iOS开发- 关于Block的几种应用
iOS开发- 关于Block的几种应用
118 0
|
自然语言处理 iOS开发
IOS——Block
IOS——Block
79 0
|
iOS开发
iOS代理 通知 block传值的规范写法
iOS代理 通知 block传值的规范写法
146 0
|
存储 API iOS开发
iOS Principle:Block(下)
iOS Principle:Block(下)
133 0
iOS Principle:Block(下)
|
C语言 iOS开发 C++
iOS Principle:Block(上)
iOS Principle:Block(上)
115 0
iOS Principle:Block(上)
|
存储 中间件 Java
iOS-底层原理 30:Block底层原理
iOS-底层原理 30:Block底层原理
285 0
iOS-底层原理 30:Block底层原理
|
机器学习/深度学习 C++ iOS开发
iOS - __block 修饰符底层探索
Block技术合集 iOS - Block变量截获 Block的写法及使用
iOS - __block 修饰符底层探索