iOS 开发 iOS Developer

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

2016-08-16  本文已影响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
*/

总结:

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
*/

总结:

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解决循环引用问题。

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的这种循环引用会被编译器捕捉到并及时提醒
  */

结论:

__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基础知识学习

上一篇下一篇

猜你喜欢

热点阅读