iOS 底层学习 -Blocks

在这里插入图片描述

Block 概念

  • Block是带有自动变量的匿名函数。如字面意思,Block没有函数名,另外Block带有插入记号"^",插入记号便于查找到Block,后面跟的一个括号表示块所需要的一个参数列表。和函数一样,可以给块传递参数,并且也具有返回值。
  • 不同点在于,块定义在函数或者方法内部,并且能够访问在函数或方法范围内的任何变量。通常情况下,这些变量能够访问但不能改变其值,有一个特殊的块修改器(由块前面由两个下划线字符组成)能够修改块内变量的值。
  • Blocks 也被称作 闭包、代码块。展开来讲,Blocks
    就是一个代码块,把你想要执行的代码封装在这个代码块里,等到需要的时候再去调用。
🌰

首先写一个简单的Block

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void(^block)(void) = ^{
            NSLog(@"B123");
        };
        block();
 }
    return 0;
}

使用命令行将代码转化为c++查看其内部结构

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

C++代码如下

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;//描述block的信息
// 构造函数(类似于OC的init方法),返回结构体对象
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp; //block内代码地址
    Desc = desc;  // 储存block对象占用的大小
  }
};

// 封装了block执行逻辑的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_7jst2vt56bd_nhbrl1hlv5s00000gn_T_main_e9907b_mi_0);
        }
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

 	// 定义block变量以及实现
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
	 // 执行block内部的代码
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);


    }
    return 0;
}
  
对比之下,原来OC下的Block相关代码
 void(^block)(void) = ^{
            NSLog(@"B123");
        };
对应的C++代码就是:
    void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

通过C++代码发现,block的调用实际上就是__main_block_impl_0这个结构体,结构体实现如下:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;//描述block的信息
// 构造函数(类似于OC的init方法),返回结构体对象
 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp; //block内代码地址
    Desc = desc;  // 储存block对象占用的大小
  }
};

__main_block_impl_0结构体内部有一个与结构体同名的__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)函数,这是C++结构体的写法,该函数为结构体的构造函数,相当于OC类中的- (instancetype)init;方法。
__main_block_impl_0函数携带三个参数

1. void *fp 
 2. struct __main_block_desc_0 *desc
 3.  int flags=0

它有两个必传的参数,一个是函数指针fp ,一个是结构体指针desc,关于结构体指针所指向的结构体就是上面分析到的__main_block_desc_0,那么第一个参数函数指针fp到底是什么?
在这个demo的C++实现代码中,fp指向的函数为__main_block_func_0,而__main_block_func_0就是block里面执行的那段代码,这里是封装成了这样一个函数:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

         NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_7jst2vt56bd_nhbrl1hlv5s00000gn_T_main_e9907b_mi_0);
        }

接下来我们来看第二个参数 struct __main_block_desc_0 *desc

static struct __main_block_desc_0 {
  size_t reserved; // 0
  size_t Block_size; //Block的内存大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

我们在执行
void(block)(void) = ((void ()())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); 传入这个函数的地址然后赋值给结构体内部变量
最后一个参数为可选的,默认值为0。
由此可得知Block本质是一个结构体,那我们看一下这个结构体内部

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;

接下来在对比一下调用代码

OC
 block();

C++
 ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

我们可以看到调用Block是先强制转换为impl,然后调用FuncPtr

🌰🌰

复杂一点的Block

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age;
        age = 30;
      void(^block)(void) = ^(){
                  NSLog(@"%d",age);
              };
        age = 40
        block();

    }

    return 0;
}

用过Block的人肯定都熟悉,打印结过为30,那我我们看一下底层实现

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
int age;

};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy

                  NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_7jst2vt56bd_nhbrl1hlv5s00000gn_T_main_440a9c_mi_0,age);
              }

可以看出Block内部生成了一个age变量用来存储,而构造block的值会将其传入内部,所以其值不会引起改变

那现在也可以很清楚Block底层的内部结构如下

在这里插入图片描述

capture

概念

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
Capture 变量,可以 Capture Block 定义所在的作用域内的变量,类似于保存上下文。之后,Block 在别处被调用时,就好像 Block 还在原本上下文一样,可以正常执行。这些都是 OC 自动做的事情。而我们在定义 Block 的时候,当前作用域内的变量,都可以在 Block 中使用。不需要顾及当 Block 在别处被调用

🧀️

我们定义三种类型的变量

  1. 全局变量
  2. 局部变量
  3. 静态变量
int  globalint ;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
      auto int stakeint = 10;
        static int staticint = 20;
      void(^block)(void) = ^(){
                  NSLog(@"%d%d%d",globalint,stakeint,staticint);
              };
       
//        void(^block)(int,int) = ^(int a,int b){
//            NSLog(@"%d",a + b);
//        };
                
        struct __main_block_impl_0 * blockstruct = (__bridge struct __main_block_impl_0*)block;
        block();

    }

C++底层
//Block结构体声明
int globalint ;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int stakeint;
  int *staticint;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _stakeint, int *_staticint, int flags=0) : stakeint(_stakeint), staticint(_staticint) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
// 调用函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int stakeint = __cself->stakeint; // bound by copy
  int *staticint = __cself->staticint; // bound by copy

       NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_7jst2vt56bd_nhbrl1hlv5s00000gn_T_main_faa523_mi_0,globalint,stakeint,(*staticint));
           }
总结一下 原理图

当我们判断是否Block会捕获对象是我们需要判断是否为全局变量,若是则不捕获,反之则捕获
在这里插入图片描述

Block的类型

看了底层实现 肯定会发现 其中有一句 如下

impl.isa = &_NSConcreteStackBlock;

这其实就是在说明Block的对象

我们先声明一个这个Block ,然后打印其父类至根类

  void(^blockTest)(void) = ^(){
                         NSLog(@"Test");
                     };
        
        NSLog(@"%@",[blockTest class]);
        //NSLog(@"%@",[[blockTest class] superclass]);
        NSLog(@"%@",[blockTest superclass]);
                  
        NSLog(@"%@",[[blockTest superclass]superclass]);
        NSLog(@"%@",[[[blockTest superclass]superclass]superclass]);

打印结果如下
在这里插入图片描述
我们可以看到最后的根类为NSObject,所以也可以说明Block是一个OC对象

类型详解

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

  • NSGlobalBlock ( _NSConcreteGlobalBlock )
  • NSStackBlock ( _NSConcreteStackBlock )
  • NSMallocBlock ( _NSConcreteMallocBlock )
Block在内存中的布局

在这里插入图片描述

Block的类型分配

在这里插入图片描述

🍪
oid (^Block)(void);
void test3() {
    int number = 10;
    Block = ^{
        NSLog(@"%d", number);
    };
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test3();
        Block();               
    }
    return 0;
}

首先说明 在ARC和MRC 会是不同的结果 ,了解过内存管理应该知道ARC是编译器特性,就是编译器帮助我们做了很多事情,简化了我们的代码量先附上MRC的打印结果
在这里插入图片描述
其次我们可以看到Block的类型为
在这里插入图片描述

而这里之所以会打印为错值,道路也很简单就是因为你之前Block结构体的内存空间在Stack 上,而test 方法使用后便会被回收,所以打印错值,那想要解决这个问题也很简单,就是我们让Block存储在堆上即可,那么根据之前的流程图,只要我们进行一次copy 操作即可
首先我们看Block的类型为
其次打印值也为10
在这里插入图片描述

🍪🍪

那我们在看第二个测试结果

  
void test3() {
    int number = 10;
    Block = [^{
        NSLog(@"%d", number);
    } copy];
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
      
     void (^BlockTest)(void)= [^{
            NSLog(@"123");
        } copy];
        NSLog(@"%@", [BlockTest class]);
    }
    test3();
    [Block copy];
     NSLog(@"%@", [Block class]);
	[Block release];
    
    return 0;
}
// BlockTest 为 __NSGlobalBlock__
// 打印结果 __NSGlobalBlock__
//     __NSMallocBlock__

ARC下的copy

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

  • block作为函数返回值时
  • 将block赋值给__strong指针时
  • block作为Cocoa API中方法名含有usingBlock的方法参数时
  • block作为GCD API的方法参数时
总结一下

每一种类型的block调用copy后的结果如下所示
在这里插入图片描述

对象类型的auto变量
🍐

一个例子引出这个

 {
        Person * person = [[Person alloc] init];
        person.age =10;
        }
        NSLog(@"1111111");
   Block block;
        {
        Person * person = [[Person alloc] init];
        person.age =10;
           block  = ^{
                NSLog(@"%d",person.age);
            };
        
        }
        NSLog(@"1111111");
打印结果 
// 第一个在打印1111111之前对象会释放
//第二个在打印1111111之前对象不会释放

那我们继续转成C++代码查看一下底层实现

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *_person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  Person *person = __cself->person; // bound by copy

                NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_7jst2vt56bd_nhbrl1hlv5s00000gn_T_main_00319d_mi_0,((int (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("age")));
            }

我们可以看到这个Block内部会生成一个person对象,而 Person *person = __cself->person,就是让Block内部的person指向 外面实例对象的person,由于外面的实例对象此时被block持有所以自然不会被释放,当Block被释放的时候,person就会被释放

🍐🍐
  Block block;
        {
        Person * person = [[Person alloc] init];
        person.age =10;
            __weak Person * weakPerson = person;
        block = ^{
                NSLog(@"%d",weakPerson.age);
            };
        
        }
        NSLog(@"1111111");
    
   // 在打印1111111之前对象会释放
       
C++底层实现
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__weak weakPerson;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  Person *__weak weakPerson = __cself->weakPerson; // bound by copy

                NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_7jst2vt56bd_nhbrl1hlv5s00000gn_T_main_9af25a_mi_0,((int (*)(id, SEL))(void *)objc_msgSend)((id)weakPerson, sel_registerName("age")));
            }
            
//我们可以发现底层person的修饰为weak 

//同时我们发现多出了两个函数
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->weakPerson, 3/*BLOCK_FIELD_IS_OBJECT*/);}


static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};


总结一下

当block内部访问了对象类型的auto变量时 如果block是在栈上,将不会对auto变量产生强引用

如果block被拷贝到堆上 会调用block内部的copy函数 copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

如果block从堆上移除 会调用block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放引用的auto变量(release)

在这里插入图片描述
说到这里我们可以看一下完整的Block的内部结构在这里插入图片描述

__block
作用

__block可以用于解决block内部无法修改auto变量值的问题

__block不能修饰全局变量、静态变量(static)

🧊
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int  age = 5 ;
        Block block = ^{
            age = 10;
            NSLog(@"%d",age);
        };
        block();
    }

那我们继续转换成C++代码

typedef void(*Block)(void);
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_age_0 *age = __cself->age; // bound by ref

            (age- g->age) = 10;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_7jst2vt56bd_nhbrl1hlv5s00000gn_T_main_526302_mi_0,(age->__forwarding->age));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};


// __block int  age = 5 ;
 __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,
 (__Block_byref_age_0 *)&age,
  0,
   sizeof(__Block_byref_age_0),
    5 };


 Block block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0,
  &__main_block_desc_0_DATA,
   (__Block_byref_age_0 *)&age,
    570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);


我们可以发现 编译器会将__block变量包装成一个对象,从而可以用于解决block内部无法修改auto变量值的问题

⚠️ 那么这里我们如果打印age是那个值呢?

__block int  age = 5 ;
    Block block = ^{
        age = 10;
        NSLog(@"%d",age);
    };
    block();
    struct __main_block_impl_0 * blockLook = (__bridge struct __main_block_impl_0*)block;
    NSLog(@"%p",&age);

在这里我们可以看到是在这里插入图片描述

(__Block_byref_age_0 *) age = 0x00000001005526b0

在这里插入图片描述
但是我们的打印结果为 相差了 24 ,我们回过头看这个结构体答案就应该显而易见了
在这里插入图片描述
就是里面的age,所以我们修改的时候是封装后的值

__block的内存管理

当block在栈上时,并不会对__block变量产生强引用

当block被copy到堆时 会调用block内部的copy函数 copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会对__block变量形成强引用(retain)

如下图
在这里插入图片描述
在这里插入图片描述

当block从堆中移除时 会调用block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放引用的__block变量(release)

在这里插入图片描述
在这里插入图片描述

__block修饰对象
🍉

先写一个简单的例子

//OC
  __block Person * person = [[Person alloc] init];
        MyBlock block = ^{
            NSLog(@"%p", person);
        };


C++
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __Block_byref_person_0 {
  void *__isa;
__Block_byref_person_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *person;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_person_0 *person; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_person_0 *person = __cself->person; // bound by ref

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_3f_7jst2vt56bd_nhbrl1hlv5s00000gn_T_main_fd7a72_mi_0, (person->__forwarding->person));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
}



我们可以了解到其内存结构如下图 在这里插入图片描述

那么是指针是强还是弱,我们可以用__weak修饰一下


   Person * person = [[Person alloc] init];
        __block __weak Person * weakperson = person;
        MyBlock block = ^{
            NSLog(@"%p", weakperson);
        };


struct __Block_byref_weakperson_0 {
  void *__isa;
__Block_byref_weakperson_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__weak weakperson;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_weakperson_0 *weakperson; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakperson_0 *_weakperson, int flags=0) : weakperson(_weakperson->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

得知__block的对象一直为强指针,但是内部指针是强还是弱取决于你的修饰,我们在看一下 __Block_byref_weakperson_的内部结构

struct __Block_byref_weakperson_0 {
  void *__isa;
__Block_byref_weakperson_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__weak weakperson;
};

发现内部也有

void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);

这其实也对其对象进行了引用技术管理

概括一下

对象类型的auto变量、__block变量
相同点

当block在栈上时,对它们都不会产生强引用

当block拷贝到堆上时,都会通过copy函数来处理它们
__block变量(假设变量名叫做a)

_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

对象类型的auto变量(假设变量名叫做p)

_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

当block从堆上移除时,都会通过dispose函数来释放它们
__block变量(假设变量名叫做a)

_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

对象类型的auto变量(假设变量名叫做p)

_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

不同点
__Block都会对其对象进行持有

__block的__forwarding指针

刚才有说到对其赋值的时候是这样的
(age->__forwarding->age) = 10;

之所以这样请看下图,从而我们不论在栈还是队列都可以访问到__block修饰的变量
在这里插入图片描述

在这里插入图片描述

_forwarding指向了自身。 在block构造的时候我们用到了a.__forwarding, 在使用的时候(__BlockTest__test_block_func_0)也是通过(a->__forwarding->a)这样的方式找到最终修改过的数值‘8’。
能做到这样的原因上面已经说过了,这里复述一下:
栈上的__block变量复制到堆上时,会将成员变量__forwarding的值替换为复制到堆上的__block变量用结构体实例的地址。所以“不管__block变量配置在栈上还是堆上,都能够正确的访问该变量”,这也是成员变量__forwarding存在的理由。参照上面的图

Block的循环引用
♻️
Block 实质
  • block本质上也是一个对象,而这个对象是一个结构体,其内部含有一个isa指针指向自己的类。
  • block是封装了函数调用以及函数调用环境的OC对象
  • block是封装函数及其上下文的OC对象。

参考的相关资料
Objecitive-C高级编程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值