Block

作者: edison0428 | 来源:发表于2018-08-01 22:43 被阅读12次

    block的基础知识

    block修饰是用strong还是copy

    对于现在而言,这两种都可以了,因为在ARC的模式下,它会把block移动到堆上

    block的分类
    • NSGlobalBlock 全局block
      void (^myBlock) (void) = ^(void){
          
            NSLog(@"this is edison ");
        };
        //能用%@ 说明block是对象 可打印
        NSLog(@"%@",myBlock);
        /*
         <__NSGlobalBlock__: 0x10c36b088>
         */
    
    • NSMallocBlockblock
     int a = 9;
        void (^myBlock) (void) = ^(void){
    
            NSLog(@"this is edison %d",a);
        };
        NSLog(@"%@",myBlock);
        /*
         <<__NSMallocBlock__: 0x604000446390>
            一个栈的变量 进入block之后就变成了malloc block 
         */
    

    一个栈的变量 进入block之后就变成了malloc block ????黑人问好??? 后面会解释

    • NSMallocBlockblock
    NSLog(@"%@",^(void){
            
            //NSLog(@"this is edison %d",a);
            NSLog(@"this is edison ");
        });
        /*
         <__NSGlobalBlock__: 0x10a927080>
    
         引入a的是NSStackBlock 没有引入a的是NSGlobalBlock
         */
    

    block的底层探讨

    __block
    image.png

    如上图所示,当我们需要在block里引用局部变量(引用全局变量不会出现这样的错误),就会报错,当然我们都知道这种解决报错的方式就是使用__block修饰局部变量,但是为什么要这么解决呢

    带着问题我们来探讨

    首先用终端创建一个.c的文件

     touch blockTest.c
     vim blockTest.c
    

    .c文件里写入如下代码,其实就是c语言的main

    #include "stdio.h"
    
    int main(){
    
    void (^block)(void) = ^(void){
    
    printf("this is edison block \n");
    }
    
    block();
    
    return 0 ;
    }
    

    然后编译.c文件

    gcc blockTest.c
    

    就会产生一个 a.out 的可执行文件

    image.png

    继续执行这个 a.out 的可执行文件

    ./a.out blockTest.c
    /*
    结果:
    this is edison block
    */
    

    这个只是执行一下 a.out 的可执行文件

    重点来了,我们继续
    clang命令

    clang  -rewrite-objc blockTest.c
    

    结果就会产生一个blockTest.cpp
    blockTest.c变成了blockTest.cppc++文件
    ,用c++的原理探讨block

    打开blockTest.cpp文件,吓一跳讲真,我们拖到最底下,找到

    int main(){
    
    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    
    return 0 ;
    }
    

    对比blockTest.c

    int main(){
    
    void (^block)(void) = ^(void){
    
    printf("this is edison block \n");
    }
    
    block();
    
    return 0 ;
    }
    

    现在改变.C

    #include "stdio.h"
      
    int main(){
    
    int a = 0;
    void (^block)(void) = ^(void){
    
    printf("this is edison block %d\n",a);
    };
    
    block();
    
    return 0 ;
    }
    

    blockTest.cpp

    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int a = __cself->a; // bound by copy
    
    
    printf("this is edison block %d\n",a);
    }
    
    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 a = 0;
    void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
    
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    
    return 0 ;
    }
    
    image.png
    就是c文件的a是在栈里的没问题吧,block被拷贝进了堆了这个也没问题吧,blockTest.cpp文件这个如上图标示出来的这段代码
    int a = __cself->a; // bound by copy  --> int a 表示重新定义了一个a 去接收传进来的__cself->a的值
    

    这里传的是a的值
    再先引进一个代码解释

     int a = 9;
        NSLog(@"a=%p",&a);
        void (^myBlock) (void) = ^(void){
    
            NSLog(@"this is edison %p",&a);
        };
        myBlock();
    /*
    2018-08-06 21:27:41.239550+0800 BlockTest[909:63152] a=0x7ffeed15bbdc
    2018-08-06 21:27:41.239748+0800 BlockTest[909:63152] this is edison 0x604000253070
    */
    

    你会发现外面的a和里面的a根本不是一个地址,说明两个a根本不一样

     __block int a = 9;//底层会做了把 栈/常量  等copy到堆区里去,并且开辟一个堆的空间 这里传的是a的指针地址
        NSLog(@"a=%p",&a);
        void (^myBlock) (void) = ^(void){
    
            NSLog(@"this is edison %p",&a);
        };
        myBlock();
    

    __block修饰的传的是a的地址
    所以如果a++这是可以的,因为a++其实修改的是地址++来达到修改外部变量的效果

    循环引用

    1

    关于block里的属性,如果是带下划_name线操作的而不是self.name的操作一样会引起循环引用,因为selfname进行了强引用

    2
    __block ViewController * weSelf = self;
        self.block3 = ^(NSString *name) {
            NSLog(@"---%@",weSelf);
        };
    

    这样能解决循环引用嘛。答案是不能
    __block的作用是把当前的内存拷贝到堆里去,那么可以手动释放

    __block ViewController * weSelf = self;
        self.block3 = ^(NSString *name) {
            NSLog(@"---%@",weSelf);
          weSelf= nil;
        };
    

    这样就可以了 ,把指向的地址置空

    3

    在block的参数里把self传进去

    self.block3 = ^(ViewController *vc) {
            NSLog(@"name=%@",vc.name);
        }
    
    4

    __weak __strong 先弱后强

    __weak __typeof(self)weakSelf = self;
        AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
    
            strongSelf.networkReachabilityStatus = status;
            if (strongSelf.networkReachabilityStatusBlock) {
                strongSelf.networkReachabilityStatusBlock(status);
            }
    
        };
    
    

    相关文章

      网友评论

          本文标题:Block

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