Block

本文详细解析了Block(匿名函数)的概念及使用方法,包括如何定义、赋值Block,以及Block内部访问不同变量的方式。此外,还介绍了Block的内部结构、循环引用问题及其常见应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   带有自动变量(局部变量)的匿名函数。(Block其实就是一个代码块,把你想要执行的代码封装在这个代码块里,等到需要时再去调用。)

   ARC下block如果访问外部变量,block在堆里,可以使用copy和strong,并且block是一个对象。

 

   定义属性:

   void (^blockName)( );//无返回值,无参数

   void (^blockName)(NSObject,string* name);//无返回值,有参数

   NSString* (^blockName)(int age,NSString* name)

   声明:

   void (^blockName)(NSString* x,int y);

   block赋值:

   block = ^返回值类型(参数列表) {函数主体}

   利用typedef为block重新命名

   typedef int (^aaa)(int a,int b);

1.Block内访问局部变量:Block定义时便是将局部变量的值传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改并不会影响Block内部的值,同时内部的值也是不可修改的. 

  //block访问普通局部变量,是传的a的值,值传递

- (void)test1

{

   int a = 10;

   void (^block)() = ^{

       NSLog(@"a is %d",a);  //10 传的a的值

   };

   a = 20;

   block();

   NSLog(@"a2 is %d",a);    //20

}

2.Block内访问__block修饰的局部变量: 在局部变量前使用__block修饰,Block定义时便是将局部变量的指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。(block块内只读)

//__block static修饰,全局变量,指针传递,传的地址。

- (void)test2

{

   __block int a  = 10;

   void (^block)() = ^{

      NSLog(@"a is %d",a);  //20 传的a的地址&a

   }; 

   a = 20;

   block(); 

   NSLog(@"a is %d",a);    //20

}

3Block内访问全局变量:全局变量所占用的内存只有一份,供所有函数共同调用,Block定义时并未将全局变量的值或者指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。

 int a = 10;

- (void)test3

{

   void (^block)() = ^{

       NSLog(@"a is %d",a);   //20  传的a的地址&a

   }; 

   a = 20;

   block(); 

   NSLog(@"a is %d",a);       //20

}

4Block内访问静态变量Block定义时便是将静态变量的指针传给Block变量所指向的结构体,因此在调用Block之前对静态变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。

- (void)test4

{

   static int a = 10;

   void (^block)() = ^{

       NSLog(@"a is %d",a);   //20  传的a的地址&a

   }; 

   a = 20;

   block(); 

   NSLog(@"a is %d",a);      //20

}

Block的内部:

1. _ _block int b = 20;  //带_ _block修饰符的block普通变量。

//每个block变量都会生成一个和OC类内存结构兼容的结构体。下面是_block int b 的结构体定义:

struct Block_b {

    void *isa;         //固定为NULL

    Block_b *forwarding;  //指向真正的block对象变量。

    int flags;

    int size;            //结构体的size

    int b;            //保存代码中定义的变量值。

};

2.  _ _block NSString *blockStr = str;   //带__block修饰符block OC变量。

 //每个block变量都会生成一个和OC类内存结构兼容的结构体。下面是 __block NString *blockStr 的结构体定义:

struct Block_blockStr {

    void *isa;  //固定为NULL

    Block_blockStr *forwarding;  //指向真正的block对象变量

    int flags;

    int size;  //结构体的size

    NSString * blockStr;  //保存代码中定义的变量值。

};

3.//定义一个block块并带一个参数

  void (^testBlock)(int) = ^(int c){

         int  d = a + b + c;

         NSLog(@"d=%d, strongStr=%@, blockStr=%@, weakStr=%@", d, strongStr, blockStr, weakStr);

     };

//每个block块都会生成一个和OC类兼容的结构体。

struct Block_testBlock {

    //前面5个数据成员在所有block定义中都相同,并且和OC兼容。

    Class isa;

    int flags;

    int reserved;

    void *funcPtr;   //block块的全局函数的地址。

    Block_testBlock_Desc* desc;   //block的描述。

 

    //所有在block代码块引用的外部数据都会成为结构体的同名数据成员。

    int a;

    NSString * strongStr;

    NSString * __weak weakStr;

    Block_b *b;

    Block_blockStr *blockStr;

 

    //结构体的构造函数。

    Block_testBlock(void *_funcPtr, Block_testBlock_Desc *_desc, int _a, NSString * _strongStr, NSString * _weakStr, struct Block_b *_b, Block_blockStr *_blockStr, int _flags)

    {

        isa = &_NSConcreteStackBlock;  //根据具体的block类型赋值。

        flags = _flags;

        reserved = 0;

        funcPtr = _funcPtr;

        desc = _desc;

        a = _a;

        strongStr = _strongStr;

        weakStr = _weakStr;

        b = _b->forwarding;  //其实就是指向自己

        blockStr = _blockStr->forwarding;

    }

};

//每个block块的描述信息结构体,主要是保存block的尺寸,以及block中函数的参数信息。

struct Block_testBlock_Desc {

    unsigned long reserved;

    unsigned long size; //块的尺寸

    void *rest[1];    //块的参数签名信息

}_testBlock_desc_DATA = {0, sizeof(Block_testBlock), "v12@?0i8"};

看出我们定义的block代码块都会生成2个结构体:

Block_testBlock用来保存block的信息以及block内部要用到的所有数据。所有block对象结构体的前5个数据成员都是一致的,也就是和OC类的内存结构是兼容的。其中的isa用来保存block的类信息,这里面的类信息会根据block所处的位置的不同而不同。而后面的5个数据成员就是在block代码块内使用外部对象的副本。正是因为每个block对象在编译时保存了代码块内使用代码块外的对象的副本,所以我们才能在后续代码执行时能够访问到这些信息。

 Block_testBlock_Desc用来描述这个block的size以及block方法的参数的签名信息。

注意:

    1.对于基本类型a的副本来说就是完全的内存拷贝,因此在block代码块内更新这些数据是不会影响到外面,同时外面的更新也不会影响到里面了。

    2.对于对象类型的strongStr和weakStr而言这个副本只是指针的拷贝而不是所指对象的拷贝,因此在block代码块内能够读取最新的属性和设置新的属性值。

3.对于__block类型的对象来说,你会发现他也是指针的拷贝,所以也不会产生多份内存副本,同时可以看出对__block类型数据的读取和设置我们都是间接来完成的,因此这里代码块内更新数据能影响外面,同时外面的更新也能影响里面。

 

block的循环引用:self->block->self

1. 如果要在block中直接使用外部强指针会发生错误。

   __weak typeof(self) weakSelf = self;

2. 如果在block内部使用延时操作还使用弱指针的话,会取不到该指针,需要在block内部在讲弱指针强引用一下。

  __strong typeof(self) strongSelf = weakSelf;


block的用途:

1.页面间传值和回调比较方便。

2.Block与多线程结合。

3.视频动画的一些API

4.  网络请求的回调

5.集合的遍历,如地区列表排序

6.  用闭包可以解决那些执行逻辑和上下文环境解耦的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值