zoukankan      html  css  js  c++  java
  • block 对外部引用变量的处理

                                  MRC 环境

    一、静态变量 和 全局变量   在加和不加  __block 都会直接引用变量地址。也就意味着 可以修改变量的值。在没有加__block 参数的情况下。

    • 全局block 和 栈block 区别为 是否引用了外部变量,堆block 则是对栈block  copy 得来。对全局block copy 不会有任何作用,返回的依然是全局block。

    二, 常量变量(NSString *a = @"hello";a 为常量变量,@“hello”为常量。)-----不加__block类型 block 会引用常量的地址(浅拷贝)。加__block类型 block会去引用常量变量(如:a变量,a = @"abc".可以任意修改a 指向的内容。)的地址。 

    如果不加__block 直接在block 内部修改变量 ,会编译报错。block 内部改变量是 只读的。

    但是 就一定可以推断  block 会深拷贝 该变量吗??? 

    对于常量 @“hello”  存储在 内存中的常量区, 程序结束才会释放 内存。  如:

    NSString *str = @"hello";

        NSString *abcStr = @"hello";

    编译器会优化处理, str 和 abcStr 都会指向 常量区的@“hello” 地址。

        NSString *str = @"hello";
        void (^print)(void) = ^{
            NSLog(@"block=str======%p",str);
        }
         str = @"hello1";
        print();

    block 会拷贝变量内容到自己的栈内存上,以便执行时可以调用。 但并不是对str 内容做了深拷贝,重新申请内存。

    因为str 是栈内存上的变量,指向 一个常量区的@“hello”. 编译器做的优化是   当block 去拷贝str 指向内容时发现是个常量,

    所以会去引用 @“hello” 的指针,没必要再取申请一块内存。

    三、对象变量 如(MyClass *class、Block block)。 这里block 也是”类“对象(类似对象,其包含isa指针,clang 反编译可以查看。因为它不像从NSObject 继承下来的对象都支持 retain、copy、release)。

    下面直接引用文章里总结的,经验证无误。

    Block的copy、retain、release操作

    不同于NSObjec的copy、retain、release操作:

    • Block_copy与copy等效,Block_release与release等效;
    • 对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
    • NSGlobalBlock:retain、copy、release操作都无效;
    • NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry addObject:stackBlock],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
    • NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
    • 尽量不要对Block使用retain操作。

    文章链接:http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/

    以下补充

    栈block : 猜测》》》》会copy 内部引用的对象变量。(如何验证 block copy 了外部变量......在block 执行前 释放对象.)。

                 但实际对两种对象变量的操作为:

    •              MyClass *class : 栈block 并不会copy 对象变量,也不会retain 对象。而是直接引用了对象变量的地址。可以在blcok 执行前释放对象验证。(有点毁三观啊) 
    •              Block block ( ”类“对象):不会对 block 做处理。如果block 是栈block ,执行时依然为栈block.     堆block 同理

        

    堆block : 通过copy 栈block 获得, 当向栈block copy 时。会对内部引用的对象变量如下处理。

    •               MyClass *class  :       block 会retain 内部引用的 对象变量,改变引用对象的内存计数。
    •               Block block( ”类“对象):   ”类“对象block 执行copy ,如果是栈block。如果为堆block 并不会对他copy 。

    GCD block :会对内部引用的对象变量如下处理。

    •                MyClass *class: retain 内部引用的对象变量,改变引用对象的内存计数。
    •                Block block( ”类“对象):   ”类“对象block 执行copy 。
        dispatch_async(dispatch_get_main_queue(), ^{
    
        });

    当然如果引用了外部栈block 变量,也会copy 栈block 到堆上。同时栈block 对其内部引用对象变量 重复前面的操作。

     这里不如说 GCD 里的 block是内部做了处理的堆block 。

    以上结论均在 MRC 环境 的   viewDidLoad  方法中测试。

    ARC环境下 

     虽然 ARC 环境下会对 栈block 做优化,当创建一个栈block 时默认返回一个 堆block 。但 并不是ARC 环境下没有栈block 。

    -(void)demo:(Block)blocka;

    -(void)demo:(Block)blocka{    

        NSLog(@"==demo=====%@",blocka);

    }

    通过 方法参数传递的 block 就是 栈block。

    由此引出 block 循环引用问题:

    循环引用 成立的条件: 直接导致A  B 两个对象相互强引用(retain,strong)、或者 间接导致 A B 两个对象相互强引用。

    直接循环引用:

    ARC 环境。

    ARC 中 LLVM 会监控对象引用情况,如果出现循环引用会waring.

    第一个警告是间接循环引用。

    • ViewController -----retain--> Custom
    • Custom -------强引用--->CustomBlock(堆block,会retain 内部引用对象变量)
    • CustomBlock  ---retain--------->self(ViewController)

    第二个警告 直接循环引用。

    • ViewController --强引用------>Block(堆block,会retain 内部引用对象变量)
    • Block------retain--------->self(ViewController)

    对于第三个 我们常用的GCD block 中,则没有出现警告。虽然 gcd block 同样对self 进行了retain.

      dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"====%@",self);
        });
        

    但是self 并没有直接或间接的去强引用 gcd block。 可以想象 gcd block 会统一被管理在GcdBlockDispatchCenter(这个是我瞎扯的,相当于GCD block 调度中心 来管理,反正不是self 。)。

    还有一种情况下不会出现循环引用 如: ARC 环境

    2014-12-30 11:05:59.119 Test_Class[4235:230795] ==demo=====<__NSStackBlock__: 0x118600>

    上面说到,栈block 并不会对内部引用对象变量 retain .而是直接引用对象变量地址。、、

    • 这里 self 和  Custom 并没有对 方法参数blocka 做任何引用操作,blocka 一直存在于栈上,直到执行完毕被释放掉。
    • 当然blocka 也没有对self  做任何操作,直接引用了地址。

     

    以上 也就是说只有堆block 才会存在循环引用的情况。

     

    》》待续.....

     

    复习下内存分类:    http://blog.csdn.net/u011848617/article/details/39346075

  • 相关阅读:
    Node项目
    Angular模块/服务/MVVM
    Angular介绍1
    Node环境配置及Gulp工具
    Linux及Git介绍
    数据库MySQL
    ReactiveCocoa 监听枚举类型enumerate 或者 NSInteger类型
    ReactiveCocoa 监听布尔(BOOL)类型改变
    python3.7 urlopen请求HTTPS警告'CERTIFICATE_VERIFY_FAILED'解决办法
    Centos yum命令
  • 原文地址:https://www.cnblogs.com/DamonTang/p/4146728.html
Copyright © 2011-2022 走看看