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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容