block

作者: 温柔的熊 | 来源:发表于2021-10-09 19:50 被阅读0次

block在objectC中是一个匿名函数。

这就是一个block

^{}

有参数列表的block

^(void){}

有参数列表和返回值的block

^void (void){}

block变量的声明

int (^block)(NSString *a, NSString *b);

这样就定义了一个block变量,它的返回值为int类型,入参为两个字符串类型。
可以对它赋值和执行。

block = ^(NSString *a, NSString *b){
 return 1;
};
int a = block();

使用typedef会使代码更简洁

typedef int (^Block)(NSString *a,NSString *b);
Block block =  ^(NSString *a, NSString *b){
  return 1;
};

作为对象属性

@property (nonatomic, copy) void (^block)(NSString *a,NSString *b);
// or
@property (nonatomic, copy) Block block;

作为函数的参数

 - (void)func:(int (^)(NSString *a, NSString *b))block;
// or
- (void)func:(Block)block;

自动变量

自动变量是指block对其上下文变量的自动捕获

int n = 1;
Block block1 = ^(NSString *a, NSString *b){
 n += 1;
 return n;
};

变量n被block自动捕获,并能在block内部被访问。
这里的n就是自动变量。

注意,上面代码中,block内部的变量n是外面声明n的一份值的拷贝。block内部对n进行赋值,不会改变外部n的值。

下面是block对变量的地址捕获

static int n = 1;
Block block2 = ^(NSString *a, NSString *b){
 n += 1;
 return n;
};

当将变量n修饰为静态变量时,block捕获的是变量的地址。
block内部修改n的值,外部n的值也会发生变化。

捕获的变量类型是对象

Person *p = [Person alloc] init];
p.age = 10;
Block block2 = ^(NSString *a, NSString *b){
 p.age = 30;
 return p.age;
};

自动变量是对象,也是地址捕获,block内部对对象的属性修改后,外部也会生效。

还有一种全局变量,block内部可以直接访问,且不会捕获。

int number = 30;
int main(){
 Block block3 = ^(NSString *a, NSString *b){
   number = 40;
   return number;
 };
}

以上是三种block:
block1 是NSStackBlock,其存储位置在栈上。
block2 是NSMallocBlock,其存储位置在堆上。
block3 是NSGlobalBlock,其存储位置在数据区。
这三种block都继承自NSBlock。

循环引用问题

// Persson.h
@property (nonatomic, assign) int age;
@property (nonatomic, copy) Block block;

// main.m
Person *p = [Person alloc] init];
p.age = 10;
p.block = ^(NSString *a, NSString *b){
 p.age = 30;
 return p.age;
};

在Person前加__weak修饰符,解除循环引用

__weak Person *p = [Person alloc] init];

__block的原理

上面说到的block1无法修改外部变量n的值。
如果想要修改,就在变量前加 __block修饰符。

__block int n = 1;
Block block1 = ^(NSString *a, NSString *b){
 n += 1;
 return n;
};
block1();
NSLog(@"n=%d",n); // 2

__block 将变量n包装成一个结构体,结构如下

struct __Block_byref_n_0 {
  void *__isa;
__Block_byref_n_0 *__forwarding; // 指向自身的指针
 int __flags;
 int __size;
 int n; // 实际值
};

__forwarding指针指向自身。
block的内部实际是通过__forwarding找到外部的n并修改其值的。

接下来模拟分析上述代码执行时,n变量在内存中的情况

__block int n = 1;
图1.png

变量n被转换成结构体,我们假设结构体变量名字为Block_n
,它的地址为0x00000000。

然后执行到下面一步

block1();
图2.png

变量n被block捕获,此时复制的是结构的值。我们假设名称为Block_n'。

注意,此时Block_n'中的__forwarding指向的是Block_n,也就是外部变量的地址。

最后执行

 n += 1;
图 3.png

外部变量n的值就被修改了。

以上内容是我对block的一点理解,如有不对之处,望能不吝斧正。

相关文章

  • iOS开发之Block原理探究

    Block概述 Block本质 Block调用 Block分类 Block循环引用 Block原理探究 Block...

  • block的使用

    定义block 返回类型 (^block名称)(参数) = ^(){block内容}; 调用block block...

  • Block 02 - __block

    Block 02 - __block __block 的作用 __block 可以解决 Block 内部无法修改 ...

  • iOS面试之Block大全

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS面试之Block模块

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS Block

    Block的分类 Block有三种类型:全局Block,堆区Block,栈区Block 全局Block 当Bloc...

  • iOS block 为什么官方文档建议用 copy 修饰

    一、block 的三种类型block 三种类型:全局 block,堆 block、栈 block。全局 block...

  • iOS开发block是用copy修饰还是strong

    Block分为全局Block、堆Block和栈Block1、在定义block没有引用外部变量的时候,block为全...

  • block 初探

    全局block, 栈block, 堆block

  • Block

    一、Block本质 二、 BlocK截获变量 三、__block 修饰变量 四、Block内存管理 五、Block...

网友评论

      本文标题:block

      本文链接:https://www.haomeiwen.com/subject/zcrqoltx.html