iOS~block的使用

作者: 猩语 | 来源:发表于2015-06-04 00:59 被阅读2969次

    iOS中block比较常用,但是又和OC的语法显得有点格格不入,难于理解。

    以下是我个人初步的理解,供查阅。 

    1.block的声明

     //声明一个block

    typedef NSString *(^WXYTestBlock)(NSString *name, int age);

    以上声明了一个名字叫做WXYTestBlock的block,参数为一个字符串类型的name和一个int类型的age,返回值为NSString。

    当然,你也可以声明成这样:

    typedef void (^WXYTestBlock)(void); 

    无参数,无返回值。

    当然也可以有参数无返回值,或者有返回值无参数,不一一列举。  

    2.block的使用 

    首先是独立block   

     //独立block    

    WXYTestBlock myBlock = ^ (NSString *name, int age){       

         return [NSString stringWithFormat:@"%@的年龄是%d",name,age];   

     };    

    NSLog(@"独立block--->%@", myBlock(@"小宇", 16));

    独立block可以直接定义和使用,运行输出如下

    2015-06-03 23:32:32.532 WXYBlock[3537:237755] 独立block--->小宇的年龄是16 

    然后是内联block

    //使用内联block的方法

    - (void)printWithName:(NSString *)name age:(int)age block:(WXYTestBlock)block{    NSLog(@"内联block--->%@",block(name, age));

    }

     内联block需要将定义的block作为参数传入相应的方法中,然后在方法中使用block。   

     //内联block   

     [self printWithName:@"王兴宇" age:26 block:^(NSString *str, int age){       

         return [NSString stringWithFormat:@"%@的年龄是%d", str, age];   

     }];

    内联block可以在调用方法的时候写入代码块,运行结果如下

    2015-06-03 23:32:32.532 WXYBlock[3537:237755] 内联block--->王兴宇的年龄是26 


    3.block使用外部变量 

    //变量的使用   

     int myAge = 100;    

    //独立block    

    myBlock = ^ (NSString *name, int age){        

        return [NSString stringWithFormat:@"使用变量--->%@的年龄是%d", name, myAge];   

     };    

    NSLog(@"独立block--->%@", myBlock(@"小宇", 16));    

    //内联block   

     [self printWithName:@"王兴宇" age:26 block:^(NSString *str, int age){       

         return [NSString stringWithFormat:@"使用变量--->%@的年龄是%d", str, myAge];  

     }]; 

    block内部可以直接使用外部定义的变量,运行结果如下

    #注意:此处为了方便,直接用myAge代替了原来的age,所以参数16传进去根本没有使用。

    2015-06-03 23:32:32.533 WXYBlock[3537:237755] 独立block--->使用变量--->小宇的年龄是1002015-06-03 

    23:32:32.533 WXYBlock[3537:237755] 内联block--->使用变量--->王兴宇的年龄是100 

    一个有趣的现象:

    现在你定义了一个独立block,并且这个block使用了外部的变量。

    然后这个变量被改变了,然后你调用了这个block。

    注意,是这样的顺序:

    定义独立block并且使用外部变量---->外部变量改变---->调用block    

    //外部改变变量    

    myAge = 50;    

    NSLog(@"独立block--->变量在外部被改变--->%@", myBlock(@"小宇", 16));        

    [self printWithName:@"王兴宇" age:26 block:^(NSString *str, int age){ 

           return [NSString stringWithFormat:@"变量在外部被改变--->使用变量--->%@的年龄是%d", str, myAge];   

    }]; 

    这时候的输出是

    2015-06-03 23:32:32.533 WXYBlock[3537:237755] 独立block--->变量在外部被改变--->使用变量--->小宇的年龄是100

    2015-06-03 23:32:32.533 WXYBlock[3537:237755] 内联block--->变量在外部被改变--->使用变量--->王兴宇的年龄是50 

    这是为什么呢?

    根据查阅,我总结的原因是这样的:

    block中如果使用了外部变量,他会拷贝一份这个变量,并且这个变量是只读的。

    所以外部变量改变并不影响block内部拷贝的那一份变量。

    代码中的内联block是在变量改变后才使用这个变量的,所以并不影响。 

    如果不想让block拷贝变量,想让内部使用的变量和外部使用的变量指向同一地址的话,

    需要在变量前面加上__block关键字。

    像这样:    

    __block int myAge = 100; 

    输出就变成了:

    2015-06-03 23:32:32.533 WXYBlock[3537:237755] 独立block--->变量在外部被改变--->使用变量--->小宇的年龄是50

    2015-06-03 23:32:32.533 WXYBlock[3537:237755] 内联block--->变量在外部被改变--->使用变量--->王兴宇的年龄是50 

    另外值得一提的是,加上__block关键字之后,外部变量不再是只读的,在block内部也可以改变它的值。

    //改变变量加__block关键字    

    __block int otherAge = 100;   

    myBlock = ^ (NSString *name, int age){

            otherAge = 99;

            return [NSString stringWithFormat:@"改变变量加__block--->%@的年龄是%d", name, otherAge];    

    };    

    NSLog(@"独立block--->%@", myBlock(@"小宇", 16));        

    [self printWithName:@"王兴宇" age:26 block:^(NSString *str, int age){

            otherAge = 98; 

            return [NSString stringWithFormat:@"改变变量加__block--->%@的年龄是%d", str, otherAge];    

    }]; 

    输出如下:

    2015-06-03 23:32:32.534 WXYBlock[3537:237755] 独立block--->改变变量加__block--->小宇的年龄是99

    2015-06-03 23:32:32.534 WXYBlock[3537:237755] 内联block--->改变变量加__block--->王兴宇的年龄是98

    #注意:如果不加__block关键字,在block内部改变外部变量的值的话,编译会报错! 

    4.block循环引用的问题 

    这部分我的理解可能不太深入,下面只说一下我自己简单的理解。

    首先在self类中声明一个NSString的属性

    #import@interface ViewController : UIViewController

    @property (strong, nonatomic) NSString *myStr;

    @end

    初始化这个属性

    self.myStr = @"myStr";

    使用内联block的另一个方法,为了方便,还用之前声明的block,只是参数用不到了

    - (void)printWithblock:(WXYTestBlock)block{

        block(@" ", 0);

    }

    现在,你想要在block中使用self,或者使用self.myStr

    如果,self的类中包含block,block中又引用了self

    这样就会造成循环引用。

    解决的方法如下

    //使用self和self的属性

    //加__weak避免循环引用

    __weak ViewController *weakSelf = self;

    //独立

    myBlock = ^ (NSString *name, int age){

    NSLog(@"独立Block使用self--->%@", weakSelf);

    NSLog(@"独立Block使用self的属性--->%@", weakSelf.myStr);

    return @" ";

    };

    myBlock(@"", 0);

    //内联

    [self printWithblock:^(NSString *name, int age){

    NSLog(@"内联Block使用self--->%@", weakSelf);

    NSLog(@"内联Block使用self的属性--->%@", weakSelf.myStr);

    return @" ";

    }];

    将self转化成为一个用__weak修饰的weakSelf,就可以避免循环引用。

    #注意:只有self中包含block的引用,并且block内使用了self才会循环引用。不过为了保险起见,所有block内用到self的还是加上__weak为好。

    以上是我个人对block至今为止全部的理解,希望对初学者有一定的帮助。

    有不足和错误之处,欢迎指正。

    相关文章

      网友评论

      • 我的大名叫小爱:好吧 培训课程都是这么讲的...
        lazysiyue: @我的大名叫小爱 6666
      • 馒头MT:文章不错,不过结尾处说到所有block的地方都用weakself,是非常不合理的.
        在一些明确的没有循环引用的地方无须使用weakself,并且我们可以通过手动释放block(参见AFNetworking里面做法)来打破循环引用.
        而且weak关键字对性能有一定的损耗,比如weak会把对象插入到weak table,在销毁的时候也做了挺多的操作,除非必要情况不应该过多使用weak.
        Auditore:@馒头MT 受教了
        猩语:多谢指点:blush::blush:正在学习中。这一块的确没有研理解的特别透彻。我在研究一下哈
      • 7720da04604f:好好学习一下
      • 猩语:@孙春磊 正在努力学习中。。
      • sclcoder:这个程度写代码基本够了

      本文标题:iOS~block的使用

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