block中加__block深刻含义

Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。---这是 微博@唐巧_boy的《iOS开发进阶》中的第11.2.3章节中的描述。

简单数据类型局部变量

block中加__block深刻含义_第1张图片
加上__block情况
  1. 在这里a是局部变量,所以是放在栈当中的。block相当于另一个内部函数,要在另一个作用域中改变栈中指针的内存地址是不行的。此时,如果block想要在内部进行操作就要将a变量拷贝到堆中,程序才能去改变。
  2. 在block中再次打印a地址,可以发现地址不一样了。这就是__block的作用,它将栈中的变量a进行copy放到堆上。(此时输出的是堆地址)
  3. 0x7开头的是在栈上的地址。0x1开头的为堆上地址。
  4. 在调用block后,a变量就已经是新的内存地址,也不再是栈上。
  5. 这里如果不加__block,a = 3就是修改a指针的内存地址。这样是不行的。

不可变对象局部变量

block中加__block深刻含义_第2张图片
NSString对象
  1. NSString为不可变字符串,初始化后不能修改内容(这里内容指的是堆上开辟的空间内容)。不加__block的情况下,string指针引用永远为初始化时的内容地址(也就是永远指向@“dddd”)。
  2. 加上__block后,在block内部会将string从栈copy到堆上。这样我们就可以操作string的指针内存地址,进行修改。也就是可以 string = @“fffff”的操作。
  3. 可以看出block以后,string的所有都copy到堆中。(特别说明:一个对象的生成,会在栈中开辟指针地址,指针地址中存放一个堆地址,这个地址就是真正存放对象内容的区域。也就是说对象都是放在堆上的。)

可变对象局部变量

(1)添加__block情况


` int b = 1;
__block NSMutableString *a = [NSMutableString stringWithString:@"Tom"];
NSLog(@"开始%@",a);
NSLog(@"b的地址为栈地址开头%p",&b);
NSLog(@"\n 定以前:------------------------------------\n
a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a); //a在栈区
void (^foo)(void) = ^{

        a.string = @"Jerry";
        NSLog(@"\n block内部:------------------------------------\n\
              a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a);               //a在栈区
        a = [NSMutableString stringWithString:@"William"];
        
        NSLog(@"block内部%@",a);
    };
    foo();
    NSLog(@"\n 定以后:------------------------------------\n\
          a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a);               //a在栈区
    
    NSLog(@"最后%@",a);`



block中加__block深刻含义_第3张图片
添加__block情况输出结果
  1. 可以发现经过block后,将a的内容全部都copy到堆上了。
  2. a = [NSMutableString stringWithString:@"William"]; 就是更改了a指针地址内容。

(2)不添加__block情况


` int b = 1;
NSMutableString *a = [NSMutableString stringWithString:@"Tom"];
NSLog(@"开始%@",a);
NSLog(@"b的地址为栈地址开头%p",&b);
NSLog(@"\n 定以前:------------------------------------\n
a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a); //a在栈区
void (^foo)(void) = ^{

        a.string = @"Jerry";
        NSLog(@"\n block内部:------------------------------------\n\
              a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a);               //a在栈区
      //  a = [NSMutableString stringWithString:@"William"];
        
        NSLog(@"block内部%@",a);
    };
    foo();
    NSLog(@"\n 定以后:------------------------------------\n\
          a指向的堆中地址:%p;a在栈中的指针地址:%p", a, &a);               //a在栈区
    
    NSLog(@"最后%@",a);`


block中加__block深刻含义_第4张图片
不添加__block情况输出结果
  1. 可变对象可以不加__block情况下,直接修改内容。
  2. a.string = @"Jerry" 修改的是堆上的内容。并不是a在栈上的指针地址。
  3. 所以最后a在堆上地址和a在栈上地址并没有改变。改变的只是a在堆上地址指向的内容。
    参考iOS中__block 关键字的底层实现原理自己总结的内容,如果有哪里有错误还请大家指教。

你可能感兴趣的:(block中加__block深刻含义)