Block

block的基础知识

block修饰是用strong还是copy

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

block的分类
//ARC 模式下
        // Global:没有访问auto变量:__NSGlobalBlock__
                void (^block1)(void) = ^{
                    NSLog(@"block1---------");
                };
                // Stack:访问了auto变量: __NSStackBlock__
                int a = 10;
                void (^block2)(void) = ^{
                    NSLog(@"block2---------%d", a);
                };
                NSLog(@"%@ %@", [block1 class], [block2 class]);
                // __NSStackBlock__调用copy : __NSMallocBlock__
                NSLog(@"%@ %@", [[block2 copy] class],[^{
                    NSLog(@"block2---------%d", a);
                } class]);
        
        //MRC 模式下
        // Global:没有访问auto变量:__NSGlobalBlock__
                void (^block4)(void) = ^{
                    NSLog(@"block4---------");
                };
                // Stack:访问了auto变量: __NSStackBlock__
                int a1 = 10;
                void (^block5)(void) = ^{
                    NSLog(@"block5---------%d", a1);
                };
                NSLog(@"MAC %@ %@", [block4 class], [block5 class]);
                // __NSStackBlock__调用copy : __NSMallocBlock__
                NSLog(@"%@ %@", [[block5 copy] class],[^{
                    NSLog(@"block2---------%d", a);
                } class]);
        
        /*
       2020-03-20 16:51:43.250229+0800 block解读[6703:829455] __NSGlobalBlock__ __NSMallocBlock__
2020-03-20 16:51:43.251585+0800 block解读[6703:829455] __NSMallocBlock__ __NSStackBlock__
         2020-03-20 16:34:07.418258+0800 block解读[6611:820683] mac __NSGlobalBlock__ __NSStackBlock__
         2020-03-20 16:34:07.418335+0800 block解读[6611:820683] __NSMallocBlock__ __NSStackBlock__
         */
  • 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)