zoukankan      html  css  js  c++  java
  • block的那些事

    block分为__NSGlobalBlock__,__NSStackBlock__,__NSMallocBlock__。

    在MRC中

    #import "NoArc.h"
    
    typedef int (^Sun)(void);
    
    @interface NoArc()
    @property (nonatomic, strong) Sun s1;
    @property (nonatomic, copy) Sun s2;
    @property (nonatomic, retain) Sun s3;
    @property (nonatomic, assign) Sun s4;
    @end
    
    @implementation NoArc
    
    -(void)blockTest{
        
        void (^SunveraAll)() = ^{
            NSLog(@"SunveraAll");
        };
        NSLog(@"SunveraAll-----%@", SunveraAll);
        __block int val =10;
        Sun s= ^{
            NSLog(@"val_block = %d", val);
            return val;
            
        };
        s();
        self.s1 = s;
        self.s2 = s;
        self.s3 = s;
        self.s4 = s;
        NSLog(@"s-----%@", s);
        NSLog(@"self.s1-----%@", self.s1);
        NSLog(@"self.s2-----%@", self.s2);
        NSLog(@"self.s3-----%@", self.s3);
        NSLog(@"self.s4-----%@", self.s4);
    }
        
    @end

    打印结果

    2018-10-01 19:48:40.996 JS_OC_WebViewJavascriptBridge[80707:1818340] SunveraAll-----<__NSGlobalBlock__: 0x10b625918>
    2018-10-01 19:48:40.997 JS_OC_WebViewJavascriptBridge[80707:1818340] val_block = 10
    2018-10-01 19:48:43.506 JS_OC_WebViewJavascriptBridge[80707:1818340] s-----<__NSStackBlock__: 0x7fff545e8488>
    2018-10-01 19:48:43.506 JS_OC_WebViewJavascriptBridge[80707:1818340] self.s1-----<__NSMallocBlock__: 0x7f94d2d285d0>
    2018-10-01 19:48:43.506 JS_OC_WebViewJavascriptBridge[80707:1818340] self.s2-----<__NSMallocBlock__: 0x7f94d2d27e80>
    2018-10-01 19:48:43.506 JS_OC_WebViewJavascriptBridge[80707:1818340] self.s3-----<__NSStackBlock__: 0x7fff545e8488>
    2018-10-01 19:48:43.507 JS_OC_WebViewJavascriptBridge[80707:1818340] self.s4-----<__NSStackBlock__: 0x7fff545e8488>

    在ARC中执行同样的代码打印结果是

    2018-10-01 19:48:45.563 JS_OC_WebViewJavascriptBridge[80707:1818340] SunveraAll-----<__NSGlobalBlock__: 0x10b625648>
    2018-10-01 19:48:45.563 JS_OC_WebViewJavascriptBridge[80707:1818340] val_block = 10
    2018-10-01 19:48:45.563 JS_OC_WebViewJavascriptBridge[80707:1818340] s-----<__NSMallocBlock__: 0x7f94d2e5f270>
    2018-10-01 19:48:45.564 JS_OC_WebViewJavascriptBridge[80707:1818340] self.s1-----<__NSMallocBlock__: 0x7f94d2e5f270>
    2018-10-01 19:48:45.564 JS_OC_WebViewJavascriptBridge[80707:1818340] self.s2-----<__NSMallocBlock__: 0x7f94d2e5f270>
    2018-10-01 19:48:45.564 JS_OC_WebViewJavascriptBridge[80707:1818340] self.s3-----<__NSMallocBlock__: 0x7f94d2e5f270>
    2018-10-01 19:48:45.564 JS_OC_WebViewJavascriptBridge[80707:1818340] self.s4-----<__NSMallocBlock__: 0x7f94d2e5f270>

    由此可见在ARC中没有栈区block,只有堆区和全局区。

    在MRC中,有栈区,并且strong相当于copy,会把block从栈区copy到堆区,用retain和assign并不会copy到堆区,个人感觉用strong和copy修饰都可以,至于为什么官方推荐block用copy来修饰,我就不太清楚了。

    block传值

    __weak

       Arc *obj = [[Arc alloc]init];
        NSLog(@"obj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&obj,obj,obj);
        
        __weak Arc *weakObj = obj;
        NSLog(@"weakObj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
        
        void(^testBlock)() = ^(){
            NSLog(@"weakObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
        };
        testBlock();
        obj = nil;
        testBlock();
    2018-10-02 10:32:01.155 JS_OC_WebViewJavascriptBridge[85577:1924012] obj变量内存地址:0x7fff55666518, 变量值:0x7fb5bb642960, 指向对象值:<Arc: 0x7fb5bb642960>
    2018-10-02 10:32:01.156 JS_OC_WebViewJavascriptBridge[85577:1924012] weakObj变量内存地址:0x7fff55666510, 变量值:0x7fb5bb642960, 指向对象值:<Arc: 0x7fb5bb642960>
    2018-10-02 10:32:01.156 JS_OC_WebViewJavascriptBridge[85577:1924012] weakObj - block变量内存地址:0x7fb5bb642dc0, 变量值:0x7fb5bb642960, 指向对象值:<Arc: 0x7fb5bb642960>
    2018-10-02 10:32:01.156 JS_OC_WebViewJavascriptBridge[85577:1924012] weakObj - block变量内存地址:0x7fb5bb642dc0, 变量值:0x0, 指向对象值:(null)

    从上面的结果可以看到

    • block 内的 weakObj 和外部的 weakObj 并不是同一个变量
    • block 捕获了 weakObj 同时也是对 obj 进行了弱引用,当我在 block 外把 obj 释放了之后,block 内也读不到这个变量了
    • 当 obj 赋值 nil 时,block 内部的 weakObj 也为 nil 了,也就是说 obj 实际上是被释放了,可见 __weak 是可以避免循环引用问题的
        Arc *obj = [[Arc alloc]init];
        NSLog(@"obj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&obj,obj,obj);
        __weak Arc *weakObj = obj;
        NSLog(@"weakObj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
        void(^testBlock)() = ^(){
            __strong Arc *strongObj = weakObj;
            NSLog(@"weakObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
            NSLog(@"strongObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&strongObj,strongObj,strongObj);
        };
        NSLog(@"weakObj-1---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
        testBlock();
        NSLog(@"weakObj-2---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
        obj = nil;
        testBlock();
        NSLog(@"weakObj-3---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
    2018-10-02 10:43:54.256 JS_OC_WebViewJavascriptBridge[85849:1930209] obj---变量内存地址:0x7fff5a694518, 变量值:0x7fb6e17470d0, 指向对象值:<Arc: 0x7fb6e17470d0>
    2018-10-02 10:43:54.258 JS_OC_WebViewJavascriptBridge[85849:1930209] weakObj---变量内存地址:0x7fff5a694510, 变量值:0x7fb6e17470d0, 指向对象值:<Arc: 0x7fb6e17470d0>
    2018-10-02 10:43:54.258 JS_OC_WebViewJavascriptBridge[85849:1930209] weakObj-1---变量内存地址:0x7fff5a694510, 变量值:0x7fb6e17470d0, 指向对象值:<Arc: 0x7fb6e17470d0>
    2018-10-02 10:43:54.258 JS_OC_WebViewJavascriptBridge[85849:1930209] weakObj - block---变量内存地址:0x7fb6e1747400, 变量值:0x7fb6e17470d0, 指向对象值:<Arc: 0x7fb6e17470d0>
    2018-10-02 10:43:54.259 JS_OC_WebViewJavascriptBridge[85849:1930209] strongObj - block---变量内存地址:0x7fff5a6943c8, 变量值:0x7fb6e17470d0, 指向对象值:<Arc: 0x7fb6e17470d0>
    2018-10-02 10:43:54.259 JS_OC_WebViewJavascriptBridge[85849:1930209] weakObj-2---变量内存地址:0x7fff5a694510, 变量值:0x7fb6e17470d0, 指向对象值:<Arc: 0x7fb6e17470d0>
    2018-10-02 10:43:54.259 JS_OC_WebViewJavascriptBridge[85849:1930209] weakObj - block---变量内存地址:0x7fb6e1747400, 变量值:0x0, 指向对象值:(null)
    2018-10-02 10:43:54.291 JS_OC_WebViewJavascriptBridge[85849:1930209] strongObj - block---变量内存地址:0x7fff5a6943c8, 变量值:0x0, 指向对象值:(null)
    2018-10-02 10:43:54.291 JS_OC_WebViewJavascriptBridge[85849:1930209] weakObj-3---变量内存地址:0x7fff5a694510, 变量值:0x0, 指向对象值:(null)

    如果你看过 AFNetworking 的源码,会发现 AFN 中作者会把变量在 block 外面先用 __weak 声明,在 block 内把前面 weak 声明的变量赋值给 __strong 修饰的变量这种写法。

    从上面例子我们看到即使在 block 内部用 strong 强引用了外面的 weakObj ,但是一旦 obj 释放了之后,内部的 strongObj 同样会变成 nil,那么这种写法又有什么意义呢?

    下面再看一段代码:

     Arc *obj = [[Arc alloc]init];
        NSLog(@"obj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&obj,obj,obj);
        __weak Arc *weakObj = obj;
        NSLog(@"weakObj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            __strong Arc *strongObj = weakObj;
            NSLog(@"weakObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
            NSLog(@"strongObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&strongObj,strongObj,strongObj);
            
            sleep(3);
            
            NSLog(@"weakObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
            NSLog(@"strongObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&strongObj,strongObj,strongObj);
        });
        NSLog(@"------ sleep 1s");
        sleep(1);
        obj = nil;
        NSLog(@"weakObj-1---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
        NSLog(@"------ sleep 5s");
        sleep(5);
        NSLog(@"weakObj-2---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&weakObj,weakObj,weakObj);
    2018-10-02 10:51:25.099 JS_OC_WebViewJavascriptBridge[86042:1934684] obj---变量内存地址:0x7fff577fc518, 变量值:0x7fa468738e80, 指向对象值:<Arc: 0x7fa468738e80>
    2018-10-02 10:51:25.100 JS_OC_WebViewJavascriptBridge[86042:1934684] weakObj---变量内存地址:0x7fff577fc510, 变量值:0x7fa468738e80, 指向对象值:<Arc: 0x7fa468738e80>
    2018-10-02 10:51:25.100 JS_OC_WebViewJavascriptBridge[86042:1934684] ------ sleep 1s
    2018-10-02 10:51:25.100 JS_OC_WebViewJavascriptBridge[86042:1934707] weakObj - block---变量内存地址:0x7fa468454da0, 变量值:0x7fa468738e80, 指向对象值:<Arc: 0x7fa468738e80>
    2018-10-02 10:51:25.100 JS_OC_WebViewJavascriptBridge[86042:1934707] strongObj - block---变量内存地址:0x7000033bfde8, 变量值:0x7fa468738e80, 指向对象值:<Arc: 0x7fa468738e80>
    2018-10-02 10:51:26.105 JS_OC_WebViewJavascriptBridge[86042:1934684] weakObj-1---变量内存地址:0x7fff577fc510, 变量值:0x7fa468738e80, 指向对象值:<Arc: 0x7fa468738e80>
    2018-10-02 10:51:26.106 JS_OC_WebViewJavascriptBridge[86042:1934684] ------ sleep 5s
    2018-10-02 10:51:28.104 JS_OC_WebViewJavascriptBridge[86042:1934707] weakObj - block---变量内存地址:0x7fa468454da0, 变量值:0x7fa468738e80, 指向对象值:<Arc: 0x7fa468738e80>
    2018-10-02 10:51:28.105 JS_OC_WebViewJavascriptBridge[86042:1934707] strongObj - block---变量内存地址:0x7000033bfde8, 变量值:0x7fa468738e80, 指向对象值:<Arc: 0x7fa468738e80>
    2018-10-02 10:51:31.108 JS_OC_WebViewJavascriptBridge[86042:1934684] weakObj-2---变量内存地址:0x7fff577fc510, 变量值:0x0, 指向对象值:(null)

    代码中使用 sleep 来保证代码执行的先后顺序。

    从结果中我们可以看到,只要 block 部分执行了,即使我们中途释放了 obj,block 内部依然会继续强引用它。对比上面代码,也就是说 block 内部的 __strong 会在执行期间进行强引用操作,保证在 block 内部 strongObj 始终是可用的。这种写法非常巧妙,既避免了循环引用的问题,又可以在 block 内部持有该变量。

    综合两部分代码,我们平时在使用时,常常先判断 strongObj 是否为空,然后再执行后续代码,如下方式:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       __strong MyObject *strongObj = weakObj;
       if (strongObj) {
           // do something ...
       }
    });

    这种方式先判断 Obj 是否被释放,如果未释放在执行我们的代码的时候保证其可用性。

    __block

        Arc *obj = [[Arc alloc]init];
        NSLog(@"obj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&obj,obj,obj);
        __block Arc *blockObj = obj;
        NSLog(@"blockObj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
        obj = nil;
        NSLog(@"blockObj-1---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
        
        void(^testBlock)() = ^(){
            NSLog(@"blockObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
            Arc *obj2 = [[Arc alloc]init];
            NSLog(@"obj2---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&obj2,obj2,obj2);
            blockObj = obj2;
            NSLog(@"blockObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
        };
        NSLog(@"%@",testBlock);
        NSLog(@"blockObj - 2---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
        testBlock();
        NSLog(@"blockObj - 3---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
    2018-10-02 11:41:10.608 JS_OC_WebViewJavascriptBridge[87017:1953201] obj---变量内存地址:0x7fff5a4e0518, 变量值:0x7ffb02d33670, 指向对象值:<Arc: 0x7ffb02d33670>
    2018-10-02 11:41:10.609 JS_OC_WebViewJavascriptBridge[87017:1953201] blockObj---变量内存地址:0x7fff5a4e0510, 变量值:0x7ffb02d33670, 指向对象值:<Arc: 0x7ffb02d33670>
    2018-10-02 11:41:10.609 JS_OC_WebViewJavascriptBridge[87017:1953201] blockObj-1---变量内存地址:0x7fff5a4e0510, 变量值:0x7ffb02d33670, 指向对象值:<Arc: 0x7ffb02d33670>
    2018-10-02 11:41:10.610 JS_OC_WebViewJavascriptBridge[87017:1953201] <__NSMallocBlock__: 0x7ffb02e46e10>
    2018-10-02 11:41:10.610 JS_OC_WebViewJavascriptBridge[87017:1953201] blockObj - 2---变量内存地址:0x7ffb02f4b378, 变量值:0x7ffb02d33670, 指向对象值:<Arc: 0x7ffb02d33670>
    2018-10-02 11:41:10.610 JS_OC_WebViewJavascriptBridge[87017:1953201] blockObj - block---变量内存地址:0x7ffb02f4b378, 变量值:0x7ffb02d33670, 指向对象值:<Arc: 0x7ffb02d33670>
    2018-10-02 11:41:10.611 JS_OC_WebViewJavascriptBridge[87017:1953201] obj2---变量内存地址:0x7fff5a4e0448, 变量值:0x7ffb02e49f40, 指向对象值:<Arc: 0x7ffb02e49f40>
    2018-10-02 11:41:10.611 JS_OC_WebViewJavascriptBridge[87017:1953201] blockObj - block---变量内存地址:0x7ffb02f4b378, 变量值:0x7ffb02e49f40, 指向对象值:<Arc: 0x7ffb02e49f40>
    2018-10-02 11:41:10.612 JS_OC_WebViewJavascriptBridge[87017:1953201] blockObj - 3---变量内存地址:0x7ffb02f4b378, 变量值:0x7ffb02e49f40, 指向对象值:<Arc: 0x7ffb02e49f40>

    可以看到在 block 声明前后 blockObj 的内存地址是有所变化的,这涉及到 block 对外部变量的内存管理问题。

    下面来看看 __block 能不能避免循环引用的问题

        Arc *obj = [[Arc alloc]init];
        NSLog(@"obj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&obj,obj,obj);
        __block Arc *blockObj = obj;
        obj = nil;
       
        void(^testBlock)() = ^(){
            NSLog(@"blockObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
        };
        obj = nil;
        testBlock();
        NSLog(@"blockObj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
    2018-10-02 11:45:05.530 JS_OC_WebViewJavascriptBridge[87107:1955309] obj---变量内存地址:0x7fff4fcdc518, 变量值:0x7fc064e30fd0, 指向对象值:<Arc: 0x7fc064e30fd0>
    2018-10-02 11:45:05.532 JS_OC_WebViewJavascriptBridge[87107:1955309] blockObj - block---变量内存地址:0x7fc064d41ee8, 变量值:0x7fc064e30fd0, 指向对象值:<Arc: 0x7fc064e30fd0>
    2018-10-02 11:45:05.532 JS_OC_WebViewJavascriptBridge[87107:1955309] blockObj---变量内存地址:0x7fc064d41ee8, 变量值:0x7fc064e30fd0, 指向对象值:<Arc: 0x7fc064e30fd0>

    当外部 obj 指向 nil 的时候,obj 理应被释放,但实际上 blockObj 依然强引用着 obj,obj 其实并没有被真正释放。因此使用 __block 并不能避免循环引用的问题。

    但是我们可以通过手动释放 blockObj 的方式来释放 obj,这就需要我们在 block 内部将要退出的时候手动释放掉 blockObj ,如下这种形式

    Arc *obj = [[Arc alloc]init];
        NSLog(@"obj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&obj,obj,obj);
        __block Arc *blockObj = obj;
        obj = nil;
       
        void(^testBlock)() = ^(){
            NSLog(@"blockObj - block---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
            blockObj = nil;
        };
        obj = nil;
        testBlock();
        NSLog(@"blockObj---变量内存地址:%p, 变量值:%p, 指向对象值:%@",&blockObj,blockObj,blockObj);
    2018-10-02 11:47:08.878 JS_OC_WebViewJavascriptBridge[87167:1956769] obj---变量内存地址:0x7fff54257518, 变量值:0x7ffd04d519a0, 指向对象值:<Arc: 0x7ffd04d519a0>
    2018-10-02 11:47:08.879 JS_OC_WebViewJavascriptBridge[87167:1956769] blockObj - block---变量内存地址:0x7ffd04c570f8, 变量值:0x7ffd04d519a0, 指向对象值:<Arc: 0x7ffd04d519a0>
    2018-10-02 11:47:08.879 JS_OC_WebViewJavascriptBridge[87167:1956769] blockObj---变量内存地址:0x7ffd04c570f8, 变量值:0x0, 指向对象值:(null)

    必须记住在 block 底部释放掉 block 变量,这其实跟 MRC 的形式有些类似了,不太适合 ARC这种形式既能保证在 block 内部能够访问到 obj,又可以避免循环引用的问题,但是这种方法也不是完美的,其存在下面几个问题

    • 当在 block 外部修改了 blockObj 时,block 内部的值也会改变,反之在 block 内部修改 blockObj 在外部再使用时值也会改变。这就需要在写代码时注意这个特性可能会带来的一些隐患
    • __block 其实提升了变量的作用域,在 block 内外访问的都是同一个 blockObj 可能会造成一些隐患

    总结!!!

    • block下循环引用的问题

      • __block本身并不能避免循环引用,避免循环引用需要在block内部把__block修饰的obj置为nil
      • __weak可以避免循环引用,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong
        的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题
    • __block与__weak功能上的区别。

      • __block会持有该对象,即使超出了该对象的作用域,该对象还是会存在的,直到block对象从堆上销毁;而__weak仅仅是将该对象赋值给weak对象,当该对象销毁时,weak对象将指向nil;
      • __block可以让block修改局部变量,而__weak不能。

    另外,MRC中__block是不会引起retain;但在ARC中__block则会引起retain。所以ARC中应该使用__weak。

    因此,__block和__weak修饰符的区别其实是挺明显的: 
    1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。 
    2.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。 
    3.__block对象可以在block中被重新赋值,__weak不可以。 
    4.__block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用,__weak只在ARC下使用,可以避免循环引用。

     http://www.cocoachina.com/ios/20180628/23965.html

  • 相关阅读:
    WinForm中AssemblyInfo.cs文件参数具体讲解
    ISO18000-6B和ISO18000-6C(EPC C1G2)标准的区别
    Win8.1下VM与Hyper-v冲突解决方法
    Mifare l卡特性简介
    【Python爬虫】beautifulsoup4库的安装与调用
    【Python爬虫】已知jpg文件url-下载图片
    【Python爬虫】测试
    【Python爬虫】Requests库的安装
    【Python】以模块化做比赛模拟
    【真随笔】未来出路,在哪里?
  • 原文地址:https://www.cnblogs.com/sunyaxue/p/9735613.html
Copyright © 2011-2022 走看看