Block

2019-06-25  本文已影响0人  六月的某一个早晨

block的变量捕获(capture)

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制

auto int age = 10;
static int height = 10;    
void (^block)(void) = ^{
    NSLog(@"age is %d,height is %d",age,height);
};        
age = 20;
height = 20;        
block();
-------------------------------------------------
output: age is 10,height is 20
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age; // 值传递
  int *height; // 指针传递
}

auto变量和static变量访问方式的不同,是由于auto变量随时可能自动销毁,通过值传递访问;而static变量会一直在内存中,可通过指针(地址)传递访问。

- (void)test
{
    void(^block)(void) = ^{
        NSLog(@"----%p",self);
    };
    block();
}
-------------------------------------------------
static void _I_YCPerson_test(YCPerson * self, SEL _cmd) {
    void(*block)(void) = ((void (*)())&__YCPerson__test_block_impl_0((void *)__YCPerson__test_block_func_0, &__YCPerson__test_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

struct __YCPerson__test_block_impl_0 {
  struct __block_impl impl;
  struct __YCPerson__test_block_desc_0* Desc;
  YCPerson *self;
};

block的类型

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

__NSGlobalBlock__ ( _NSConcreteGlobalBlock )
__NSStackBlock__ ( _NSConcreteStackBlock )
__NSMallocBlock__ ( _NSConcreteMallocBlock )
block类型 环境
NSGlobalBlock 没有访问auto变量
NSStackBlock 访问了auto变量
NSMallocBlock NSStackBlock调用了copy

每一种类型的block调用copy后的结果如下所示

Block的类 副本源的配置存储域 复制效果
_NSConcreteGlogalBlock 程序的数据区域 什么也不做
_NSConcreteStackBlock 从栈复制到堆
_NSConcreteMallocBlock 引用计数器增加

对象类型的auto变量

YCPerson *person = [[YCPerson alloc] init];
person.age = 10;        
__weak YCPerson *weakPerson = person;
    MyBlock block = ^{
    NSLog(@"-----%d",weakPerson.age);
};
-------------------------------------------------
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  YCPerson *__weak weakPerson;
}
YCPerson *person = [[YCPerson alloc] init];
person.age = 10;
//        __weak YCPerson *weakPerson = person;
MyBlock block = ^{
    NSLog(@"-----%d",person.age);
};
-------------------------------------------------
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  YCPerson *__strong person;
}

当block内部访问了对象类型的auto变量时

函数 调用时机
copy函数 栈上的Block复制到堆上
dispose函数 堆上的Block被废弃时

__block修饰符

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 三种auto变量
        int no = 20; // 基本数据类型的auto变量,不需要内存管理,不会生成copy函数和dispose函数

        __block int age = 10; // __block修饰基本数据类型变量,需要内存管理,会生成copy函数和dispose函数

        NSObject *object = [[NSObject alloc] init]; // 对象类型的auto变量,需要内存管理,不会生成copy函数和dispose函数
        __weak NSObject *weakObject = object;
     
        // 刚开始block内存在栈上,在ARC环境下,一旦block被强引用着,会对栈上的block进行copy操作,会拷贝到堆上
        // block如果是在栈上,对象类型的auto变量object和__block变量age产生的都是弱引用,不是强引用;如果block被copy到堆时,都会通过copy函数来处理它们
        MyBlock block = ^{ 
            age = 20; // 修改age变量
            NSLog(@"%d",no);
            NSLog(@"%d",age);
            NSLog(@"%p", weakObject);
        };
        block();
    }
    return 0;
}
-------------------------------------------------
struct __Block_byref_age_0 {
    void *__isa;
    struct __Block_byref_age_0 *__forwarding;
    int __flags;
    int __size;
    int age;
};

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

struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    struct __Block_byref_age_0 *age;
};

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age; // 结构体内部会存储着age值
};

  // 此处block会捕获三个auto变量
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int no; // 将no值直接存储
  NSObject *__weak weakObjc; // 访问对象类型的auto变量,会在内部存储该类型的指针变量,__weak or __strong取决于外部如何访问
  __Block_byref_age_0 *age; // 访问__Block变量,会将age包装到__Block_byref_age_0结构体中,block内部保留一个引用这个结构体的指针,指针会指向__Block_byref_age_0结构体
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _no, NSObject *__weak _weakObjc, __Block_byref_age_0 *_age, int flags=0) : no(_no), weakObjc(_weakObjc), 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; // 通过block中age指针拿到指向结构体的指针
  int no = __cself->no; // bound by copy
  NSObject *__weak weakObjc = __cself->weakObjc; // bound by copy
            (age->__forwarding->age) = 20;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_dw_1_y0148j4xd12nhm7z0r2gj00000gn_T_main_161189_mi_0,no);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_dw_1_y0148j4xd12nhm7z0r2gj00000gn_T_main_161189_mi_1,(age->__forwarding->age));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_dw_1_y0148j4xd12nhm7z0r2gj00000gn_T_main_161189_mi_2,weakObjc);
        }

// block从栈拷贝到堆时调用
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
// __block,第三个参数传递8(BLOCK_FIELD_IS_BYREF),__block变量不存在强弱引用之分,就是强引用
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
// 对象类型的auto变量object,第三个参数传递3(BLOCK_FIELD_IS_OBJECT),如果外部通过弱(强)引用访问OC对象,那_Block_object_assign对OC对象产生的就是弱(强)引用
_Block_object_assign((void*)&dst->weakObjc, (void*)src->weakObjc, 3/*BLOCK_FIELD_IS_OBJECT*/);}

// block从堆中移除调用
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->weakObjc, 3/*BLOCK_FIELD_IS_OBJECT*/);}

// 访问对象类型的auto变量,会生成以下两个函数,对内部访问的对象进行内存管理操作,访问基本数据类型不会生成这两个函数
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的forwarding指针

__block的forwarding指针
(age->__forwarding->age) = 20;

block循环引用问题

// __weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil,若再次访问此变量,不会产生错误。
__weak typeof(self) weakSelf = self;
   self.block = ^{
   NSLog(@"%d",weakSelf.age);
};
// __unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变,若再次访问此变量,容易产生野指针错误。
__unsafe_unretained typeof(self) weakSelf = self;
   self.block = ^{
   NSLog(@"%d",weakSelf.age);
};
__weak、__unsafe_unretained解决循环引用.png
__block YCPerson *person = [[YCPerson alloc] init];
person.age = 10;
person.block = ^{
    NSLog(@"%d",person.age);
    person = nil;
};
person.block(); // 必须要调用block,执行block内代码,将对象置为nil
用__block解决循环引用.png
上一篇下一篇

猜你喜欢

热点阅读