OC Block 代码块

前言

  • Block 是一段预先准备好的代码,可以在需要的时候执行,可以当作参数传递。Block 可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。Block 是 C 语言的,类似于一个匿名函数,它和传统的函数指针很类似,但是 Block 是 inline(内联函数)的,并且默认情况下它对局部变量是只读的。

  • 苹果官方建议尽量多用 Block。在多线程、异步任务、集合遍历、集合排序、动画转场用的很多。

  • Block 语法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // Block as a local variable
    returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};

    // Block as a property
    @property (nonatomic, copy) returnType (^blockName)(parameterTypes);

    // Block as a method parameter
    - (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;

    // Block as an argument to a method call
    [someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];

    // Block as typedef
    typedef returnType (^TypeName)(parameterTypes);
    TypeName blockName = ^returnType(parameters) {...};

1、Block 的使用

1.1 Block 的定义

  • Block 的简单定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 定义 Block
    /*
    定义了一个名叫 MySum 的 Block 对象,它带有两个 int 型参数,返回 int 型。等式右边就是 Block 的具体实现,大括号后需加分号
    */
    int (^MySum)(int, int) = ^(int a, int b){

    return a + b;
    };

    // 调用 Block
    int sum = MySum(10, 12);
  • Block 数据类型的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 定义 block 数据类型 MyBlock
    typedef int (^MyBlock)(int, int);

    // 定义 MyBlock 的变量
    MyBlock myblock;

    // 实现 MyBlock 的变量 1
    myblock = ^(int a, int b){

    return a + b;
    };

    // 调用 MyBlock 的变量 1
    int sum = myblock(10, 12);

    // 实现 MyBlock 的变量 2
    myblock = ^(int a, int b){

    return a - b;
    };

    // 调用 MyBlock 的变量 2
    int minus = myblock(13, 2);

1.2 Block 访问局部变量

  • Block 可以访问局部变量,但是不能修改,如果要修改需加关键字 __block(双下划线)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 这样定义时,局部变量 sum 只能读取值不能修改,编译时会报错
    // int sum = 10;

    // 这样定义时,局部变量 sum 既可以读取值又能修改
    __block int sum = 10;

    int (^MyBlock)(int) = ^(int a){

    // 对局部变量值修改
    sum ++;

    // 读取局部变量的值
    return a * sum;
    };

    int result = MyBlock(5);

2、Block 的回调

2.1 Block 回调使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Block1.h

// block 属性变量定义
/*
要使用 copy 类型,格式:@property (nonatomic, copy) 返回值类型 (^变量名) (参数类型列表);
*/
@property (nonatomic, copy) void (^completion) (NSString *);

// 调用 block 代码段声明
- (void)useBlock;

// Block1.m

// 调用 block 代码段
- (void)useBlock {

// 设置 block 的回调值

// 判断是否设置了 block
if (self.completion != nil) {

// 设置回调值
self.completion(@"hello world");
}
}

// Block.m

#import "Block1.h"

Block1 *block = [[Block1 alloc] init];

// 设置 block 代码段
block.completion = ^(NSString *str) {

// 结果:string = @"hello world"
NSString *string = str;
};

// 调用 block 代码段
[block useBlock];

2.2 Block 回调封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Block2.h

// block 方法参数定义

// 类方法定义
+ (Block2 *)blockWithCompletion:(void (^) (NSString *)) completion;

// 调用 block 代码段声明
- (void)useBlock;

// Block2.m

// block 属性变量定义

// 要使用 copy 类型,格式:@property (nonatomic, copy) 返回值类型 (^变量名) (参数类型列表);
@property (nonatomic, copy) void (^completion) (NSString *);

// 调用 block 代码段
- (void)useBlock {

// 设置 block 的回调值

// 判断是否设置了 block
if (self.completion != nil) {

// 设置回调值
self.completion(@"hello world");
}
}

// 类方法实现
+ (Block2 *)blockWithCompletion:(void (^)(NSString *))completion {

Block2 *bl = [[Block2 alloc] init];

// 设置属性的值
bl.completion = completion;

return bl;
}

// Block.m

#import "Block2.h”

// 设置 block 代码段
Block2 *block = [Block2 blockWithCompletion:^(NSString *str) {

// 结果:string = @"hello world"
NSString *string = str;
}];

// 调用 block 代码段
[block useBlock];

3、Block 属性定义中为什么使用 copy 修饰

  • ARC 开发的时候,编译器底层对 block 做过一些优化,使用 copy 修饰可以防止出现内存泄漏。

  • 从内存管理的角度而言,程序员需要管理的内存只有堆区的。如果用 strong 修饰,相当于强引用了一个栈区的变量。

  • 而使用 copy 修饰,在设置数值的时候,可以把局部变量从栈区复制到堆区。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 用 copy 修饰定义属性
    @property (nonatomic, copy) void (^myTask)();

    // 定义,myBlock 是保存在栈区的,出了作用域,就应该被销毁
    void (^myBlock)() = ^ {

    NSLog(@"hello");
    };

    // 用属性记录
    self.myTask = myBlock;

    // 执行
    self.myTask();

4、循环引用

  • 在 Block 中调用 self 容易产生循环引用,无法释放对象,在程序中可以使用析构方法判断是否产生了循环引用。

    1
    2
    3
    4
    5
    6
    7
    8
    @implementation ViewController

    // 在 Block 中调用 self 容易产生循环引用
    [[QWebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) {
    self.image = image;
    }];

    @end
    1
    2
    3
    4
    // 判断是否存在循环引用,无法释放时即存在循环引用
    - (void)dealloc {
    NSLog(@"成功退出");
    }
  • 可以使用关键字 __weak 声明一个弱变量,或者为属性指定 weak 特性。如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @implementation ViewController

    // 弱引用 self,typeof(self) 等价于 ViewController
    __weak typeof(self) weakSelf = self;

    [[QWebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) {
    weakSelf.image = image;
    }];

    @end
文章目录
  1. 1. 前言
  2. 2. 1、Block 的使用
    1. 2.1. 1.1 Block 的定义
    2. 2.2. 1.2 Block 访问局部变量
  3. 3. 2、Block 的回调
    1. 3.1. 2.1 Block 回调使用
    2. 3.2. 2.2 Block 回调封装
  4. 4. 3、Block 属性定义中为什么使用 copy 修饰
  5. 5. 4、循环引用
隐藏目录