iOS归纳

iOS - Block的写法及使用

2021-05-01  本文已影响0人  码代码的小马

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的代码封装起来。


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();
8. 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");
    };
上一篇下一篇

猜你喜欢

热点阅读