Block

作者: bytebytebyte | 来源:发表于2021-04-14 14:48 被阅读0次
    1.block本质是封装了函数调用以及函数调用环境的oc对象。自己要会根据源码分析出这个结论记忆会更深刻。
    函数调用:函数地址
    函数调用环境:block 访问外部的变量
    OC对象:有isa指针
    
    struct __main_block_desc_0 {
        size_t reserved;
        size_t Block_size;
    };
    
    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;
        int age;
    };
            int age = 20;
            void (^block)(int, int) =  ^(int a , int b){
                NSLog(@"this is a block! -- %d", age);
                NSLog(@"this is a block!");
                NSLog(@"this is a block!");
                NSLog(@"this is a block!");
            };
            struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;
            block(10, 10);
    
    2.请分析为什么打印的值是10?
    auto int age = 10; //定义出来的局部变量都是auto,可以省略不写,意思是离开所在的大括号会自动销毁
     block = ^{
            // age的值捕获进来(capture)
            NSLog(@"age is %d", age);
      };
    age = 20;
    block();
    
    因为block具备捕获的能力,在block内已经把10这个值传递给block内部的成员变量了,所以打印10,而这个时候去修改age的值其实是修改的block外部的age。
    捕获:block内部会新增一个成员变量正好是外部的变量
    
    3.为什么自动变量是值传递不是指针传递?
    因为自动变量在函数执行完后会销毁,销毁后再去访问就是垃圾数据了.
    什么是自动把变量?
    局部变量分2种:auto、static
    auto int age = 10; //定义出来的局部变量都是auto,auto只能修饰局部变量,可以省略不写,意思是离开所在的大括号会自动销毁
    
    void (^block)(void);
    void test()
    {
        auto int age = 10;
        static int height = 10;
        block = ^{
            // age的值捕获进来(capture)
            NSLog(@"age is %d, height is %d", age, height);
        };
        age = 20;
        height = 20;
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            test();
            block(); //age is 10, height is 20
          }
    }
    
    4.全局变量为什么不用捕获可以直接访问?
    因为谁都可以随时访问就没有必要访问了。
    为什么局部变量需要捕获?test已经结束了,block又去访问,要想访问到需要先把值存在block即捕获,即跨函数访问的原因。
    
    5.block变量捕获的种类?3种
    变量类型                                           捕获到block内部        访问方式
    局部     auto 基本数据类型                          ✔️                      值传递
                auto 对象类型                                  ✔️                 连同其所有权修饰符的指针传递
    变量     static                                                ✔️                     指针传递
    全局    变量                                                  ×                        直接访问
    
    究竟block会不会捕获只要搞清楚是局部变量还是全局变量就行了
    
    6.下边例子会不会捕获?
    会。只要搞清楚是局部变量还是全局变量就行了,因为OC函数默认为传递2个参数(MJPerson * self, SEL _cmd)函数的调用者和方法名。
    Person.m
    - (void)test {
              void(^block)(void) = ^ {
                      nslog(@"%@",self);
                       nslog(@"%@",_name);//self->name //直接将self捕获,不是单独不会_name,看_name来源,同理[self name]也一样
              }
    }
    
    7.block类型有哪几种?
             block类型                                  环境
    __NSGlobalBlock__                  没有访问auto变量
    __NSStackBlock__                     访问了auto变量
    __NSMallocBlock__          __NSMallocBlock__调用了copy
    
    每种block调用copy后的结果:
                   block的类                            副本源的配置存储域                    复制结果
    __NSConcreteGlobalBlock__                          栈                                         堆
    __NSConcreteStackBlock__                         数据段                             什么也不做
    __NSConcreteMallocBlock__                          堆                                  引用计数增加
    
    8.ARC下block自动copy的情况有哪些?
    在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
    (1)block作为函数返回值时
    typedef void (^MJBlock)(void);
    MJBlock myblock()
    {
        int age = 10;
        return ^{
            NSLog(@"---------%d",age);
        };
    }
    main() {
            MJBlock block = myblock();
            block();
            NSLog(@"%@", [block class]); //__NSMallocBlock__
    }
    
    (2)将block赋值给__strong指针时
            int age = 10;
            MJBlock block = ^{
                NSLog(@"---------%d", age);
            };
            NSLog(@"%@", [block class]); //__NSMallocBlock__
    
    (3)block作为Cocoa API中方法名含有usingBlock的方法参数时
    [arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                        
            }];
    (4)block作为GCD API的方法参数时
    dispatch_once(&onceToken, ^{
                
            });
    
    9.栈和堆访问对象类型的auto变量时都发生了什么?
    栈block不会对auto变量产生强引用;
    block被拷贝到堆上,block会调用copy函数
    
    10.打印什么?怎么解释?-什么时候被释放的问题?
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        MJPerson *p = [[MJPerson alloc] init];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"1-------%@", p);
        });
        NSLog(@"touchesBegan:withEvent:");
    }
    /*
    touchesBegan:withEvent:
    1-------<MJPerson: 0x6000008f04b0>
    MJPerson - dealloc
    */
    block作为GCD的参数会发生copy,将栈上的block复制到堆上;
    MJPerson *p是对象类型的强引用,在堆block内部会强引用p,在1s后才会释放block,在释放block前会先释放block强引用的p
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        MJPerson *p = [[MJPerson alloc] init];
    __weak MJPerson *weakP = p;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-------%@", weakP);
        });
        NSLog(@"touchesBegan:withEvent:");
    }
    /*
    touchesBegan:withEvent:
    MJPerson - dealloc
    2-------(null)
    */
    block作为GCD的参数会发生copy,将栈上的block复制到堆上;
    在大括号结束时p 会被释放掉;
    MJPerson *p是对象类型的弱引用,在堆block内部会弱引用p,在1s后才会释放block,而此时weakP已被释放所以null。
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        MJPerson *p = [[MJPerson alloc] init];
        __weak MJPerson *weakP = p;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"1-------%@", p);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"2-------%@", weakP);
            });
        });
        NSLog(@"touchesBegan:withEvent:");
    }
    2021-04-11 11:36:34.831422+0800 Interview03-测试[17362:9913914] touchesBegan:withEvent:
    2021-04-11 11:36:34.831549+0800 Interview03-测试[17362:9913914] MJPerson - dealloc
    2021-04-11 11:36:35.929587+0800 Interview03-测试[17362:9913914] 2-------(null)
    2个block整体看待即可,2s后被释放。
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        MJPerson *p = [[MJPerson alloc] init];
        __weak MJPerson *weakP = p;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"1-------%@", weakP);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"2-------%@", p);
            });
        });
        NSLog(@"touchesBegan:withEvent:");
    }
    2021-04-11 13:41:36.149167+0800 Interview03-测试[17689:9981812] touchesBegan:withEvent:
    2021-04-11 13:41:37.149242+0800 Interview03-测试[17689:9981812] 1-------<MJPerson: 0x6000028b4230>
    2021-04-11 13:41:39.342495+0800 Interview03-测试[17689:9981812] 2-------<MJPerson: 0x6000028b4230>
    2021-04-11 13:41:39.342795+0800 Interview03-测试[17689:9981812] MJPerson - dealloc
    2个block整体看待即可,第2个block强引用p,3s后被释放。
    
    总结:看强引用什么时候被释放。就是几秒后被销毁。
    
    11.MRC和ARC下block写法是怎么样的?
    MRC下block属性的建议写法
    @property (copy, nonatomic) void (^block)(void);
    
    ARC下block属性的建议写法:以下2中写法等价
    @property (strong, nonatomic) void (^block)(void); ARC下也会拷贝到堆上
    @property (copy, nonatomic) void (^block)(void);
    
    12.有哪些方式可以解决在block内部修改外部变量的值?改为__block,static修饰,全局变量
    为什么不用static修饰,全局变量呢?因为static、全局变量会一直在内存中,只是希望在内存中暂时存一下。
    
    13.理解__block变量
    __block可以用于解决block内部无法修改auto变量值的问题
    __block不能修饰全局变量、静态变量(static)
    编译器会将__block变量包装成一个对象
    
    __block int age = 10;
    ^{
            nslog(@"%d",age);
    }();
    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
    }
    
    14.block访问__block基本数据类型、对象类型变量、__block对象类型的区别?
    强引用;
    由其修饰符决定;
    block对__block变量结构体强引用,结构体对对象类型变量由其修饰符决定。
    
            __block int age = 10;
            NSObject *object = [[NSObject alloc] init]; //这种修饰符默认strong  
            __weak NSObject *weakObject = object;  //这种修饰符默认weak
            MJBlock block = ^{
                NSLog(@"%d", age);  //block强引用object
                NSLog(@"%p", weakObject); //block弱引用weakObject
            };
           block();
    
    block对__block变量结构体强引用,ARC下结构体对对象类型变量由其修饰符决定,
                                                             MRC下结构体对对象类型变量不会强引用。
    ARC:
    (1)strong情况:
    MJBlock block;
            {
               __block MJPerson *person = [[MJPerson alloc] init];
                block = ^{
                    NSLog(@"%p", person);// block内部person结构体的person指针默认是 strong  
                };
            }
            block();
            //0x1060575b0
    (2)weak情况:
            MJBlock block;
            {
                MJPerson *person = [[MJPerson alloc] init];
                __block __weak MJPerson *weakPerson = person;
                block = ^{
                    NSLog(@"%p", weakPerson);//block内部person结构体的person指针默认是 weak
                };
            }
            block();
    2021-04-11 23:14:48.230528+0800 Interview01-__block[19517:10315770] -[MJPerson dealloc]
    2021-04-11 23:14:48.231074+0800 Interview01-__block[19517:10315770] 0x0
    
    MRC:
    main(){
            __block MJPerson *person = [[MJPerson alloc] init];
            MJBlock block = [^{
                NSLog(@"%p", person);
            } copy];
            [person release];
            block();
            [block release];
    }
    2021-04-11 23:29:27.590414+0800 Interview01-__block[19583:10326096] -[MJPerson dealloc]
    2021-04-11 23:29:27.591165+0800 Interview01-__block[19583:10326096] 0x10051bae0
    MRC下person被释放了block内部还能访问,说明是弱引用
    
    总结;block对__block变量(基本数据类型、对象类型)结构体一定是强引用。
    
    15、__block变量的内存管理
    栈block不会对__block变量(基本数据类型、对象类型)强引用;
    当栈block被copy到堆上时,调用block内部copy函数对__block变量(基本数据类型、对象类型)强应用,
    当栈block被移除时,调用block内部dispose函数对__block变量(基本数据类型、对象类型)release.
    
    __Block_byref_id_object_copy、__Block_byref_id_object_dispose是对__Block_byref_weakPerson_0内的MJPerson *__weak weakPerson。
    
    (1)对象类型:__main_block_desc_0里copy()、dispose(),对对象类型内存管理
    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*); //copy函数在desc结构体里
      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};
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc; ///////////////////看这里///////////////////////
      MJPerson *__strong person;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MJPerson *__strong _person, int flags=0) : person(_person) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    (2)__block 基本类型:__main_block_desc_0里copy()、dispose(),对__Block_byref_age_0内存管理
    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;
      NSObject *p;
      (struct)__Block_byref_age_0 *age; // by ref ///////////////////block对__block 基本类型强引用,C++中可以省略struct///////////////////////
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {////////////////////////////__main_block_copy_0///////////////////////////////
        _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
        _Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    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->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __main_block_desc_0 { //////////////copy、dispose在desc里//////////////
      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};
    
    (3)__block 对象类型:__main_block_desc_0里copy()、dispose(),对__Block_byref_age_0内存管理,
    __Block_byref_weakPerson_0里的__Block_byref_id_object_copy、__Block_byref_id_object_dispose对__Block_byref_weakPerson_0内的MJPerson *__weak weakPerson内存管理;
    
    struct __main_block_desc_0 {//////////////////////////////__main_block_desc_0///////////////////////////////
        size_t reserved;
        size_t Block_size;
        void (*copy)(void);
        void (*dispose)(void);
    };
    
    struct __Block_byref_weakPerson_0 {////////__Block_byref_weakPerson_0//////////////////
      void *__isa; // 8
    __Block_byref_weakPerson_0 *__forwarding; // 8
     int __flags; // 4
     int __size; // 4
     void (*__Block_byref_id_object_copy)(void*, void*); // 8 ///__Block_byref_id_object_copy//////////
     void (*__Block_byref_id_object_dispose)(void*); // 8 ///__Block_byref_id_object_dispose//////////
     MJPerson *__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;
      }
    };
    
    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, 8/*BLOCK_FIELD_IS_BYREF*/);
    }
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {
        _Block_object_dispose((void*)src->weakPerson, 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};
    
    16.理解__forwarding指针:
    栈上__block变量结构体的__forwarding 指向自己本身;
    发生复制后:此时
    栈上__block变量结构体的__forwarding 指向堆上__block变量结构体;
    堆上__block变量结构体的__forwarding 指向自己本身;
    总结:没发生复制__forwarding指向自己,发生了复制__forwarding指向堆上__block变量的结构。体,即没复制是自己,复制了就是堆区的结构体。
    
    17.__weak和__unsafe_unretained区别?
    __weak不会产生强引用,指向的对象销毁时,会自动让指针置为nil
    __unsafe_unretained不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变。
    
    18.block循环引用的解决方式有哪些?
    ARC:__weak、__block 、__unsafe_unretained(不建议用)。
    MRC:__block 、__unsafe_unretained(不建议用)。MRC不支持__weak的
    
    __weak typeof(self) weakSelf = self;
    __unsafe_unretained id weakSelf = self; 
    self.block = ^{
          printf(@"%p",weakSelf);
    }
    
    __block  id weakSelf = self;
    self.block = ^{
          printf(@"%p",weakSelf);
          weakSelf = nil;
    }
    self.block()
    缺点:不调用self.block()会产生循环引用,进而造成内存泄漏。
    
    19.block在修改NSMutableArray,需不需要添加__block?不需要。
    NSMutableArray *array = [NSMutableArray array];
    person.block = ^{
          [arr addObject:@"1"];
    } ;
    
    20.__block作用是什么?注意点?
    修改局部变量的值,在MRC下block不会对__block变量结构体强引用。
    
    21.block属性修饰词为什么是copy?使用block有哪些注意点?
    copy在堆上可以控制block的生命周期,对block进行内存管理。
    注意循环引用。
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    相关文章

      网友评论

          本文标题:Block

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