通过黑盒去猜想和验证ARC 大环境下OC中Block 都做了什么

和往常一样提出问题:

1.ARC 环境下中__block 这个关键字做了什么事情

2.ARC 环境下中__weak 这个关键字做了什么事情

3.block 捕获block 外部的变量的时候,对外部变量做了什么事情

4.为什么在block 里无法修改外部变量

5 为什么加上了__block 修饰变量以后 又可以改变了

带着这几个问题,我们来看一段代码

  NSString *str =@"12345";

    NSLog(@"--1-->%p",str);

    void(^block1)() =^{

        NSLog(@"---2->%p",str);

        

    };

    NSLog(@"--3-->%p",str);

    str = @"134";

    NSLog(@"--4-->%p",str);

    block1();

你可以猜想一下输出结果,哪些和哪些相同!下面我们来看结果

2017-02-22 16:57:58.500 HZCache[84338:2708224] --1-->0x106281250

2017-02-22 16:57:58.500 HZCache[84338:2708224] --3-->0x106281250

2017-02-22 16:57:58.500 HZCache[84338:2708224] --4-->0x1062812d0

2017-02-22 16:57:58.501 HZCache[84338:2708224] ---2->0x106281250


我们现在输出的是 @"12345" 和 @"134"实际存储内存地址,我们可以看出来1,2,3相同,4不同,下面我们再看一段代码

NSString *str = @"12345";

    NSLog(@"--1-->%p",&str);

    void(^block1)() =^{

        NSLog(@"---2->%p",&str);

        

    };

    NSLog(@"--3-->%p",&str);

    str = @"134";

    NSLog(@"--4-->%p",&str);

    block1();

这段代码输出的是str 所在的位置各位猜想一下,这时候哪些相同哪些不同(废话不多讲我们直接上结果)

2017-02-22 17:03:33.066 HZCache[84430:2712719] --1-->0x7fff5cc9ab48

2017-02-22 17:03:33.066 HZCache[84430:2712719] --3-->0x7fff5cc9ab48

2017-02-22 17:03:33.066 HZCache[84430:2712719] --4-->0x7fff5cc9ab48

2017-02-22 17:03:33.067 HZCache[84430:2712719] ---2->0x60000004f020

上面结果表明1,3,4 相同2不同。这是不是可以猜想出来一下的内容

NSString *str = @"12345"; 这句话在内存分配上做了三件事情,为str 分配一块内存,为@"12345"分配一块内存,将@"12345"内存地址放入到str 的内存中。那么针对block 做了什么事情,我们可以猜想block 捕捉外部变量的时候,其实只是在block 里声明了一个与外部同名的同类型的变量,而这个变量值指向的就是@"12345"的内存地址,所以当str 被赋值@“134”的时候,block 输出的值还是@"12345",因为它指向的内存地址并没有改变!至于结果是不是我猜想的那样我们看一下代码来验证一下

  NSString *str =@"12345";

    NSLog(@"--1-->%@",str);

    void(^block1)() =^{

        NSLog(@"---2->%@",str);

        

    };

    NSLog(@"--3-->%@",str);

    str = @"134";

    NSLog(@"--4-->%@",str);

    block1();

输出结果

2017-02-22 17:29:38.396 HZCache[84841:2731851] --1-->12345

2017-02-22 17:29:38.396 HZCache[84841:2731851] --3-->12345

2017-02-22 17:29:38.396 HZCache[84841:2731851] --4-->134

2017-02-22 17:29:38.396 HZCache[84841:2731851] ---2->12345

这个例子是不是支持了我们的猜想了呢,那又有一个问题为什么在block 无法对str进行重新赋值呢?

下面我再看一个例子:

 

NSMutableString *str = [[NSMutableStringalloc] initWithString:@"12345"];

    NSLog(@"--1-->%@",str);

    void(^block1)() =^{

        str = 

[[NSMutableString allocinitWithString:@"12345"];

        [str appendString:@"134"];

        NSLog(@"---2->%@",str);

    };

    block1();

    NSLog(@"--3-->%@",str);

    str = [[NSMutableStringalloc] initWithString:@"123"];

    NSLog(@"--4-->%@",str);

    block1();


你们这代码有问题吗?如果有问题出现在哪?如果没问题请问输出结果是啥?下面就是结果了

2017-02-22 17:48:58.647 HZCache[85177:2744586] --1-->12345

2017-02-22 17:48:58.647 HZCache[85177:2744586] ---2->12345134

2017-02-22 17:48:58.647 HZCache[85177:2744586] --3-->12345134

2017-02-22 17:48:58.647 HZCache[85177:2744586] --4-->123

2017-02-22 17:48:58.647 HZCache[85177:2744586] ---2->12345134134


会出现这个结果的原因其实很好解释

[str appendString:@"134"]; 这句操作的是str 指向的地址里面的内容,而不是str 地址的内容所以它就不会有问题,从而也可以解释上面的输出结果,那么还有一个程序

 NSMutableString *str = [[NSMutableStringalloc] initWithString:@"12345"];

    NSLog(@"--1-->%@",str);

    void(^block1)() =^{

       str = [[NSMutableString alloc] initWithString:@"123"];

        NSLog(@"---2->%@",str);

    };

    block1();

    NSLog(@"--3-->%@",str);

    str = [[NSMutableStringalloc] initWithString:@"123"];

    NSLog(@"--4-->%@",str);

    block1();

请问上面的会报错吗,结果肯定会报错啊,因为你在block 里修改了是 str 指针,而不是修改str指针指向的内存。我们可以猜想一下,blcok 其实给我们隐藏了一行代码如下

 累死的代码呢NSMutableString *const str = str;实际上它真的是添加了累死的代码!有写人会说这不就是浅copy吗?只是copy 指针不copy内容,怎么说呢?它确实这样的,但是它真的没有调用对象copy 方法,我用下面的例子来证明这个问题

 NSMutableString *str = [[NSMutableStringalloc] initWithString:@"12345"];

    NSLog(@"--1-->%p",[strcopy]);

    void(^block1)() =^{

        NSLog(@"---2->%p",str);

    };

    NSLog(@"--3-->%p",str);

    str = [[NSMutableStringalloc] initWithString:@"123"];

    NSLog(@"--4-->%p",str);

    block1();

输出结果是

2017-02-22 18:03:32.838 HZCache[85475:2756246] --1-->0xa000035343332315

2017-02-22 18:03:32.838 HZCache[85475:2756246] --3-->0x600000078a80

2017-02-22 18:03:32.838 HZCache[85475:2756246] --4-->0x60800007b680

2017-02-22 18:03:32.839 HZCache[85475:2756246] ---2->0x600000078a80

这个问题例子告诉我们 NSMutableString copy 操作也是深度复制所以我是不是可以说blcok 捕捉外部变量并没有对外部变量执行copy操作,那么有人也会说你这是验证了字符串,有没有验证NSArray 这种结合,下面我通过下面的例子来验证一下NSArray 和 NSMutableArray 来验证我的想法

 

NSString *str1 = @"123456";

    NSString *str2 = @"123";

    NSArray *array = [[NSArray alloc] initWithObjects:str1,str2, nil];

    void(^block)() = ^{

         NSLog(@"---1-->%p",array);

        NSLog(@"---4-->%p",&array);

    };

    NSLog(@"---2-->%p",array);

    NSLog(@"---5-->%p",&array);

    block();

    NSLog(@"---3-->%p",array);

    NSLog(@"---6-->%p",&array);

结果是

2017-02-23 09:47:48.097 HZCache[87003:2826674] ---2-->0x600000034340

2017-02-23 09:47:48.097 HZCache[87003:2826674] ---5-->0x7fff56439b38

2017-02-23 09:47:48.097 HZCache[87003:2826674] ---1-->0x600000034340

2017-02-23 09:47:48.097 HZCache[87003:2826674] ---4-->0x60000004bd20

2017-02-23 09:47:48.098 HZCache[87003:2826674] ---3-->0x600000034340

2017-02-23 09:47:48.098 HZCache[87003:2826674] ---6-->0x7fff56439b38

我们比较1,2,3 是否相同,4是否与5,6不同,这样同样验证我的猜想!
我们可以通过下面例子来验证是否会影响NSArray 里的元素

 NSString *str1 = @"123456";

    NSString *str2 = @"123";

    NSArray *array = [[NSArray alloc] initWithObjects:str1,str2, nil];

    void(^block)() = ^{

        NSString *tempStr  = array[0];

         NSLog(@"---1-->%p",tempStr);

    };

    NSLog(@"---2-->%p",str1);

    block();

    NSLog(@"---3-->%p",str1);

}

结果是

2017-02-23 09:52:16.514 HZCache[87094:2830177] ---2-->0x10e64a250

2017-02-23 09:52:16.515 HZCache[87094:2830177] ---1-->0x10e64a250

2017-02-23 09:52:16.515 HZCache[87094:2830177] ---3-->0x10e64a250

说明这个block 不会影响NSArray 的内容,那么 block 对NSMutableArray 的影响你们可以自己去试试!下面我们需要试试加上__block 以后的影响废话不多说直接上例子:

__block NSString *str1 = @"123456";

     NSLog(@"-str1指向的内存地址1--->%p",str1);

     NSLog(@"---str1的内存地址5--->%p",&str1);

    void(^block)() = ^{

        NSLog(@"--str1指向的内存地址2--->%p",str1);

        NSLog(@"---str1的内存地址6--->%p",&str1);

    };

     NSLog(@"-str1指向的内存地址3--->%p",str1);

    NSLog(@"----str1的内存地址7-->%p",&str1);

    block();

    NSLog(@"--str1指向的内存地址4--->%p",str1);

    NSLog(@"----str1的内存地址8-->%p",&str1);

大家可以猜想一下结果,结果真的无法想象的!

2017-02-23 10:08:41.142 HZCache[87431:2845147] -str1指向的内存地址1--->0x103f1c270

2017-02-23 10:08:41.142 HZCache[87431:2845147] ---str1的内存地址5--->0x7fff5bce7b48

2017-02-23 10:08:41.143 HZCache[87431:2845147] -str1指向的内存地址3--->0x103f1c270

2017-02-23 10:08:41.143 HZCache[87431:2845147] ----str1的内存地址7-->0x6080000505b8

2017-02-23 10:08:41.143 HZCache[87431:2845147] --str1指向的内存地址2--->0x103f1c270

2017-02-23 10:08:41.143 HZCache[87431:2845147] ---str1的内存地址6--->0x6080000505b8

2017-02-23 10:08:41.143 HZCache[87431:2845147] --str1指向的内存地址4--->0x103f1c270

2017-02-23 10:08:41.143 HZCache[87431:2845147] ----str1的内存地址8-->0x6080000505b8

发现了什么,678 的结果是一致的,5不同,1,2,3,4始终一致,为什么出现这样的情况呢,就是因为我们加了__block 我们改变了block 以及block 之后的是str1变量的内存地址,也就是说,block 依然给我们申明了一个变量来存放了外部的变量,并且这个变量作用范围是这个block 周围不再单单是block 里面了,那么为什么会出现这样的情况了,我们从程序编译上来解释一下吧,其实每行代码就是一组命定的集合,程序编译的时候都是从上到下编译的,当遇到block 类型的时候,编译器会去检测block 是否捕捉了外部的变量,如果捕捉了,那么这个变量是否被__block 修饰,如果被修饰了我们把block 里,我们需要改变整个str1 所在的内存地址,那么问题来了为什么用__block 修饰以后我们就可以改变 str1指向的内存地址,而未用__block 修改的时候确不能更改呢?其实很好解释,__block 作用就是改变作用域!我想应该可以解释过去了.其实有我们可以重复上面的验证方法,基本上可以总结出来block 里捕捉外部变量,声明了一个变量来指向外部变量指向的内存地址,这个也很好解释,并不是所有的block 里使用self 都会导致循环应用,它有可能只是会延长self 生命周期而已!
还有一个问题就是__weak 对block 做了什么,其实在ARC 中我知道和弱持有和强持有的问题,其实__weak 和 不加任何修饰词唯一区别是,__weak 就是在内部持有的隐形变量对外部变量指向的内存是弱应用,其余没有啥区别



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值