文章

block

官方文档

Block 的存储区域

__weak 与 __strong 是如何解决循环引用的


block 的本质

通过 clang -rewirite-objc main.m 命令,查看生成的 main.cpp 文件,可以看出:

  • 编译器会将 block 的内部代码生成对应的函数,调用 block 时就是调用这个函数


block 的内存管理

  • 默认情况下, block 是在栈内存中,不会对所引用的对象进行任何操作
  • 如果对 block 做一次 copy,或者 block 内部引用了外部变量,则 block 就会被拷贝到堆内存中


block 的使用

  • 定义 block 类型属性时要用copy,这样才会把 block 放在堆内存中管理
  • block 内部不能修改外部没有用__block修饰的对象,但可以修改这个对象的属性
  • __block修饰变量,则该变量可以在 block 内被修改
  • __weak修饰变量,可以解决 block 循环引用的问题
  • __strong修饰变量,可以防止 block 中引用的外部变量在 block 执行时被提前释放的问题


1
2
3
// 在 ARC 中防止循环引用,需要将 self 或其他变量进行 weak 化,__typeof(self)是获取 self 的类型
__weak MyController *weakSelf = self 
__weak typeof(self) weakSelf = self;



  • 如果 block 内部代码不是异步执行,则此时的 strongSelf 变量是可以省略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// strongSelf 是 block 内部的一个局部变量,作用域仅限于 block 内部,程序一旦跳出作用域,
// strongSelf 就会被释放,这个临时产生的“循环引用”就会被自动打破,不会对 self 一直进行
// 强引用,其作用就是为了让 self 在 block 的生命周期中不会被提前释放

__weak typeof(self) weakSelf = self;  
self.block = ^{  
    __strong typeof(weakSelf) strongSelf = weakSelf; // 不可以省略
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
       [NSThread sleepForTimeInterval:5];  
        [strongSelf print]; 
   });  
};



__weak typeof(self) weakSelf = self;  
self.block = ^{  
    __strong typeof(weakSelf) strongSelf = weakSelf; // 可以省略
    [strongSelf print];  
};



1
2
// 直接定义一个 block 属性,index 是参数,无返回值
@property (nonatomic, copy) void(^readSuccess)(NSInteger index);



1
2
3
4
// 先定义一个 block,然后再使用
typedef void(^FuckSuccess)(void);
。。。
@property (nonatomic, copy) FuckSuccess success;



1
2
3
4
5
6
7
8
9
10
11
12
13
NSString *aString = @"Hey!";
void(^aBlock)(void) = ^{
    // 因 aString 没有用 __block 关键字修饰,在 block 中无法被修改
    
    NSLog(@"%@", aString);
};
aString = @"fuck";
    
// 只有当 aString 用 __block/static 修饰或者定义为全局变量时,在定义 block 之后修改 aString 变量才有效,
// 否则在 block 中取到的 aString 值是在定义该 block 之前的值
aBlock();
    
NSLog(@"%@", aString);



  • aString 没有用 __block 修饰时:
1
2
2018-04-02 22:41:11.596957+0800 Test_3[12376:1852822] Hey!
2018-04-02 22:41:11.597102+0800 Test_3[12376:1852822] fuck


  • aString 有用 __block 修饰时:
1
2
2018-04-02 22:41:42.692088+0800 Test_3[12401:1854704] fuck
2018-04-02 22:41:42.692210+0800 Test_3[12401:1854704] fuck


引用官方文档:

Use __block Variables to Share Storage

If you need to be able to change the value of a captured variable from within a block, you can use the __block storage type modifier on the original variable declaration. This means that the variable lives in storage that is shared between the lexical scope of the original variable and any blocks declared within that scope.

大致意思是:用 __block 修饰的变量,存活在 原始变量的词法范围 和 声明的 block 之间共享的存储中。

本文由作者按照 CC BY 4.0 进行授权