闭包与 Objective-C block | 青训营笔记

86 阅读3分钟

这是我参与「第四届青训营」笔记创作活动的的第4天

1 闭包基础

闭包本身是一个结构体,存储了函数入口地址和与之绑定的一系列环境,环境里面既有其本身内部定义的变量,又包含从外部捕获的变量,捕获变量的处理因实现而异。

2 Block基础

2.1 简单例子

// block声明
returnType (^blockName)(parameters);
// block赋值
^returnType(parameters) {
  // do something;
};
// 示例
int (^sumBlock)(int a, int b) = ^int(int a, int b) {
  return a + b;
};
int sum = sumBlock(1, 1);

省略写法

// 有返回值有入参
int (^sumBlock)(int, int) = ^(int a, int b) {
    return a + b;
};
// 无返回值无入参
void (^printBlock)(void) = ^{
    NSLog(@"Hello World!");
};

3 Block内存管理

image.png

ARC机制,在一些默认的情况下系统会帮你自动调用copy操作:

  • block作为函数返回值
  • 将block赋值给strong指针
  • block作为某些系统方法参数

3.1 变量捕获

3.1.1 值捕获

- (void)changeValue {
    int value = 1;

    void (^oneBlock)(void) = ^{
        NSLog(@"value = %d", value); // value1:1
    };

    value = 2;
    oneBlock();
    NSLog(@"value = %d", value); // value2:2
    
    NSString *value = @"1";
    void (^oneBlock)(void) = ^{
        NSLog(@"value = %@", value); // value1:1
    }
    value = @"2";
    oneBlock();
    NSLog(@"value = %@", value); // value2:2
}

由于oneBlock在初始化的时候,按值捕获了value,而后,当value更改后,block内捕获的值并没有跟着一起变化;而value2处则是使用的改变后的变量。

3.1.2 引用捕获

- (void)changeValue {
    __block int value = 1;

    void (^oneBlock)(void) = ^{
        NSLog(@"value = %d", value); // value1:2
        value = 3;
    };

    value = 2;
    oneBlock();
    NSLog(@"value = %d", value); // value2:3
}
    
@property (nonatomic, copy) NSString *name;

- (void)changeValue {
    self.name = @"1";
    void (^oneBlock)(void) = ^{
        NSLog(@"value = %@", self.name); // value1:2
    }
    self.name = @"2";
    oneBlock();
    NSLog(@"value = %@", self.name); // value2:2
}

想要修改传入的值并生效: 上面的代码块和之前的很像,区别在于传入的变量前增加了一个关键字__block,这个关键字标志了该变量允许在block中被修改,这里block内部对于value的捕获是引用捕获,也就是说block中的value就是外部的value对象,因此对应值的改变也会同步,内外互相影响

3.2 Block的循环引用

在ARC下,系统会在某些场景默认对block执行copy操作,使其变为__NSMallocBlock__,此时block也会有自己的内存引用计数。

简单例子

// ViewController.m

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) void (^completionBlock)(void);

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.completionBlock = ^{
        NSLog(@"%@", self.name);
    };
}

@end

解决方法

__weak typeof(self) weakSelf = self; 
self.completionBlock = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf; 
    NSLog(@"%@", strongSelf.name); 
};

4 Block的应用

4.1 网络请求中数据传递

// AFHTTPSessionManager.h

- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                            parameters:(nullable id)parameters
                              progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

网络请求传入url、网络参数以及进度、成功和失败的回调block,这三个block就是主要负责数据传递的

AFHTTPSessionManager *manager = AFHTTPSessionManager.manager;
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"image/jpeg", nil];

__weak typeof(self) weakSelf = self;
NSString *url = @"<http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg>";

// 开始下载
[manager GET:url parameters:nil progress:^(NSProgress *downloadProgress) {
    NSLog(@"progress:%lld",downloadProgress.completedUnitCount);
} success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"图片下载完成");
    __strong typeof(weakSelf) strongSelf = weakSelf;
    strongSelf.imgView.image = [UIImage imageWithData:(NSData *)responseObject];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    if (error) {
        NSLog(@"%@",error.userInfo);
    }
}];

5 小结

本节课学习了在OC里面重要的Block,对Block的基础知识,基础应用有了一个简单了解,Block在开发过程中对数据的传递起到了重要作用

6 引用参考

用大白话🙌带你掌握闭包 - 掘金

OC Block - 掘金

闭包与 Objective-C block