美文网首页iOS 开发 iOS Developer
第二节 block知识学习(copy,__weak,__bloc

第二节 block知识学习(copy,__weak,__bloc

作者: 柳声 | 来源:发表于2016-08-16 18:28 被阅读0次

    Dear All 这节我们来学习block知识 ,废话不说 、让我们直奔主题

    1. __block关键字的作用 (基本数据类型)
    /*对于基本数据类型的变量
      如果是局部的:
      在声明前不加__block,那么在block中用到的该变量是对原来值的一个copy(在给block块分配内存空间的时候),该block是不允许更改该变量的(不允许写)。
      所以编译时候会报错( variable is not assignable (missing __block type specifier ))。
      在声明前加__block,在block使用的过程中不会对原来的值进行copy,可以直接读写该变量
      如果是全局的或是静态的
      那么不会copy该变量的值
     */
    //int static sum = 10;
    - (void)__blockTest{
    
        //int sum = 5;  //( variable is not assignable (missing __block type specifier ))
        __block int sum = 5;
        NSLog(@"1---%p --- %d", &sum, sum);
        sum = 10;
        void (^sumBlock) (int m, int n) = ^(int m, int n) {
            sum = m + n;
            NSLog(@"2---%p --- %d", &sum, sum);
        };
        sum = 15;
        sumBlock(1, 2);
        NSLog(@"3---%p --- %d", &sum, sum);
        
        __block NSString *name = @"小明";
        NSLog(@"1---%@---%p", name, &name);
        void (^stringBlock) (void) = ^(void) {
            name = @"小丽";
            NSLog(@"2---%@---%p", name, &name);
        };
        name = @"ls";
        stringBlock();
        NSLog(@"3---%@---%p", name, &name);
    }
    /*
      非ARC  输出结果
      11---0x7fff5aeb3888 --- 5
      22---0x7fff5aeb3888 --- 3
      33---0x7fff5aeb3888 --- 3
      1---小明---0x7fff5aeb3828
      2---小丽---0x7fff5aeb3828
      3---小丽---0x7fff5aeb3828
    */
    /*
      ARC 输出结果
      1---0x7fff5b2fa828 --- 5
      2---0x7f9a42783d98 --- 3
      3---0x7f9a42783d98 --- 3
      1---小明---0x7fff5b2fa7c8
      2---小丽---0x7f9a42728478
      3---小丽---0x7f9a42728478
    */
    
    

    总结:

    • 如果想在block内修改某局部变量需加__block, MRC 环境下block在使用过程中不会对原来值进行copy,可以直接修改该变量 ,ARC环境下会对原值进行copy,内存地址也发生变化。

    • block可以直接修改 全局静态变量 ,不会copy该变量的值。(代码中没给出、大家可以自己打印看下) 。

    2.__block关键字的作用 (指针类型的变量)

    - (void)__blockObjTest
    {
        //对于指针类型的变量
        //如果不加__block,block中是对(原来指针的copy),也就是说有两个不同的指针,指向同一个对象,在block中可以更改对象的属性值,但是不可以更改对象(可以对指向的对象改变其属性,不能把原来的线断掉再建立一个新线)
        //如果添加__block,block中不会对原来的指针进行copy,所以可以更改属性,也可以更改其值
        //如果是全局的,或者是静态的,则不会copy指针
        
        __block People *people = nil;
        people = [[People alloc] init];
        people.name = @"zhangsan";
        
        NSLog(@"1---%@---%p", people, &people);
        
        void (^peopleBlock) (void) = ^(void) {
            NSLog(@"2---%@---%p", people, &people);
            people.name = @"wangwu";
            /*
            people = [[People alloc] init];
            people.name = @"zhaoliu";
            */
        };
        people.name = @"lisi";
        peopleBlock();
        NSLog(@"3---%@---%p", people, &people);
    }
    /*
      MRC 环境下打印结果:
      //不使用 __block 
      1---<People: 0x7f9c4bc11fd0>---0x7fff57f1c708
      2---<People: 0x7f9c4bc11fd0>---0x7fff57f1c6f8
      3---<People: 0x7f9c4bc11fd0>---0x7fff57f1c708
       
     //使用 __block 
      1---<People: 0x7f8c72775ce0>---0x7fff59240708
      2---<People: 0x7f8c72775ce0>---0x7fff59240708
      3---<People: 0x7f8c72607320>---0x7fff59240708
    */
    
    /*
      ARC 环境下打印结果:
      //不使用 __block 
      1---<Person: 0x7f84a9ff6280>---0x7fff52527828
      2---<Person: 0x7f84a9ff6280>---0x7f84a9e07080
      3---<Person: 0x7f84a9ff6280>---0x7fff52527828
       
     //使用 __block 
      1---<Person: 0x7fb090704970>---0x7fff5a905828
      2---<Person: 0x7fb090704970>---0x7fb090553938
      3---<Person: 0x7fb090704970>---0x7fb090553938
    */
    

    总结:

    • 不加__block, MRC 和 ARC block中都是对(原来指针的copy),也就是有两个不同的指针,指向同一个对象。
    • 使用__block ,
    • MRC环境block中不会对原来的指针进行copy,所以可以更改属性,也可以更改对象本身 。
    • ARC环境则是对原对象的copy,内存地址也发生变化。
    • block可以直接修改指针类型 全局静态变量 ,不会copy该变量的值。(代码中没给出、大家可以自己打印看下) 。

    4.__weak关键字的作用

    接下来我们学习一下 weak 关键字,先上代码

    - (void)weakTest
    {
        Person *p = [[Person alloc]init];
        p.name = @"myObject";
    
        NSLog(@"1---%@---%p--%@", p, &p,p.name);
        __weak Person *weakObj = p;
        NSLog(@"2---%@---%p--%@", weakObj, &weakObj,weakObj.name);
       
        void(^testBlock)() = ^(){
              NSLog(@"3---%@---%p--%@", weakObj, &weakObj,weakObj.name);
    //        NSLog(@"3---%@---%p--%@", p, &p,p.name);
        };
        
        testBlock();
        p = nil; // 这边值nil 用来判断block是否复制了对象
        testBlock();
    }
    /*
      ARC 环境下打印结果:(MRC 是没有__weak关键字的)
      //不使用 __weak 
      1---<Person: 0x7fbf82505070>---0x7fff5ea9b828--myObject
      2---<Person: 0x7fbf82505070>---0x7fff5ea9b820--myObject
      3---<Person: 0x7fbf82505070>---0x7fbf82417330--myObject
      3---<Person: 0x7fbf82505070>---0x7fbf82417330--myObject
    
      //使用 __weak
      1---<Person: 0x7f87d3f0ca90>---0x7fff5811a828--myObject
      2---<Person: 0x7f87d3f0ca90>---0x7fff5811a820--myObject
      3---<Person: 0x7f87d3f0ca90>---0x7f87d3e0df30--myObject
      3---(null)---0x7f87d3e0df30--(null)
    */
    

    总结:

    • 不使用 __weak, p = nil 后block块内打印出的对象仍不为空,说明block中都是对原对象的copy。

    • 使用 __weak p = nil 后person对象为nil ,说明block是对(原来指针的copy),也就是有两个不同的指针,指向同一个对象,对象释放后 weakObj 也不在持有, 并会被置nil 防止野指针报错。

    __weak解决循环引用问题。

    typedef void(^Block1)(NSString *str );
    
    #import "ViewController.h"
    
    @interface ViewController ()
    
    @property(nonatomic,copy)Block1 block;
    
    @end
    
    - (void)__weakTest
    {
        // __weak typeof(self)weakSelf = self;
         self.block = ^(NSString *name){
        //  NSLog(@"arr:%@", self.arr);
        //  [self weakTest];
        //  p.name = @"haha";
        };
        self.block(@"123");
    }
    
    /*
     block在copy时都会对block内部用到的对象进行强引用(ARC)或   者retainCount增1(非ARC)。
     在ARC与非ARC环境下对block使用不当都会引起循环引用问题 。
     一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,
     简单说就是self.block = ^(Type var){
                                                  [self dosomething];
                                              或者  self.otherVar = XXX;
                                              或者  _otherVar = ...
                                            };
        block的这种循环引用会被编译器捕捉到并及时提醒
      */
    

    结论:

    • 某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身, block的这种循环引用会被编译器捕捉到并及时提醒。解决方法 __weak typeof(self)weakSelf = self;

    __weak 可能产生的问题分析........ 试想 weakSelf 指针是没有对象持有权的,那么外部对象被提前释放了怎么办?block内部的执行岂不是会出错 ?这个问题又当如何解决呢? 神奇的 strong 关键字来了, 先看代码

    -(void)strongTest
    {
        Person* p = [[Person alloc]init];
        p.name = @"myObject";
        NSLog(@"1---%@---%p--%@", p, &p,p.name);
    
        __weak Person *weakObj = p;
        NSLog(@"2---%@---%p--%@", weakObj, &weakObj,weakObj.name);
        
        void(^testBlock)() = ^(){
            __strong Person *strongObj = weakObj;
            NSLog(@"3---%@---%p--%@", strongObj, &strongObj,strongObj.name);
        };
        testBlock();
        p = nil;
        testBlock();
    }
    /*
      1---<Person: 0x7f9410f7e7f0>---0x7fff5e28f828--myObject
      2---<Person: 0x7f9410f7e7f0>---0x7fff5e28f820--myObject
      3---<Person: 0x7f9410f7e7f0>---0x7fff5e28f748--myObject
      3---(null)---0x7fff5e28f748--(null)
    */
    

    发现 __strong 修饰的对象仍被置nil 了 怎么回事呢 ?? 接着看.....

    -(void)strongTestC
    {
        Person* p = [[Person alloc]init];
        p.name = @"myObject";
        
        __weak Person *weakObj = p;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
            __strong Person *strongObj = weakObj;
            
            NSLog(@"1---%@---%p--%@", strongObj, &strongObj,strongObj.name); //先打印这行
           
            sleep(3); // 睡眠三秒确保 p 被置 nil 后执行接下来的代码
            
            NSLog(@"3---%@---%p--%@", weakObj, &weakObj,weakObj.name);
            NSLog(@"3---%@---%p--%@", strongObj, &strongObj,strongObj.name);
      
        });
        
        sleep(1); //睡眠1秒让异步线程block块执行
        
        p = nil;  //执行过程中将 p 对象置 nil 
        
        NSLog(@"2---%@---%p--%@", weakObj, &weakObj,weakObj.name);
        
        sleep(4); //异步线程结束后 再打印出 person 对象
        
        NSLog(@"4---%@---%p--%@", weakObj, &weakObj,weakObj.name);
    }
    /*
        1---<Person: 0x7f828248f120>---0x700000116df8--myObject
        2---<Person: 0x7f828248f120>---0x7fff57f35820--myObject
        3---<Person: 0x7f828248f120>---0x7f82824aa1c0--myObject
        3---<Person: 0x7f828248f120>---0x700000116df8--myObject
        4---(null)---0x7fff57f35820--(null)
    */
    

    结论:
    p = nil 后由__strong 修饰的对象仍然存在。
    说明,再block执行过程中,如果对象用 __strong 修饰 block内部依然会继续强引用它 。
    那么上面的例子也就不奇怪了,因为下面的代码是后续执行的,所以打印出结果为 nil 。

    block 内部的 __strong 会在执行期间进行强引用操作,保证在 block 内部 strongObj 始终是可用的。这种写法非常巧妙,既避免了循环引用的问题,又可以在 block 内部持有该变量
    我们平时在使用时,常常先判断 strongObj 是否为空,然后再执行后续代码,如下方式

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           __strong Person *strongObj = weakObj;
           if (strongObj) { 
                //
           }
    });
    

    6 . block变量定义时为什么用copy关键字
    默认情况下,block是存档在栈中,可能被随时回收,故需要copy操作。这也就是我们在定义block的时候用得时copy (arc 下也可以用strong), 而不是weak等。

    原因是Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。

    NSGlobalBlock:类似函数,位于text段;
    NSStackBlock:位于栈内存,函数返回后Block将无效;
    NSMallocBlock:位于堆内存
    

    具体请自行谷歌(网上太多了)。
    总结 ,这节主要回顾了__weak __block __stong 的用法 ,内容还是挺复杂的 要仔细观看哦 Learning together is Better

    第一节 block基础知识学习

    相关文章

      网友评论

        本文标题:第二节 block知识学习(copy,__weak,__bloc

        本文链接:https://www.haomeiwen.com/subject/tjohsttx.html