block的使用浅析

作者: 堂吉诃德灬 | 来源:发表于2016-08-16 20:58 被阅读110次

    1 block的基本概念

    1.1 block的产生和用途

    代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量、作为参数、作为返回值,特殊地,Block还可以保存一段代码,在需要的时候调用,目前Block已经广泛应用于iOS开发中。Block实际上是OC语言对闭包的实现,是带有自动变量值的匿名函数。Block既可以定义在函数内部也可以定义在函数外部。只有在调用block的时候,block{}内的代码才会执行。

    1.2 闭包的概念

    闭包就是一个函数,或者一个指向函数的指针,加上这个函数执行的非局部变量。闭包允许一个函数访问声明该函数运行上下文中的变量,甚至可以访问不同运行上文中的变量。

    1.3 block的声明与定义

     int (^myBlock)(int) = ^(int num){
    
    return num*num;
    
    };
    

    int 代表返回类型 ^代表的这是一个block

    myBlock代表block的名字 (int)代表参数类型

    (int num)代表形参的类型及名字

    在定义block的时候一定不要忘记在最后加上分号

    block是一个对象,内部是一个结构体,在创建block 的时候就把它的指针传给了block,所以可以直接调用block块

    但是为了使用block方便,一般在定义block的时候会用typedef简化block如:typedef void(^myBlock)(int) ;

    1.4 block的实质

    通俗点说,block是一个结构体,它里面包含了函数指针以及block外部上下文变量等信息,这个函数指针指向的是在定义block时的代码块。

    1.5 block最常使用的情况

    block最常使用的方式:界面传值;回调

    2 Block对变量的访问与修改

    2.1 block对局部变量的访问与修改

    (1) block1 = ^{
            NSLog(@"哈哈哈哈哈哈");
        };
        block1();
    
    
    (2) NSInteger aaa = 100;
        block2 = ^{
            aaa++;    //会报错
            NSLog(@"aaa的值为%ld",aaa);
        };
    
    
    (3)__block NSInteger aaa = 100;
        block2 = ^{
            aaa++;  
            NSLog(@"aaa的值为%ld",aaa);   //aaa = 101
        };
    
    
    (4) NSInteger aaa = 100;
        block5 = ^{
            NSLog(@"aaa的值为%ld",aaa);//aaa = 100
        };
        aaa = 105;
        block5();
    
    
    (5)__block NSInteger ddd = 10;
        block7 = ^{
            ddd = ddd +1;
            NSLog(@"ddd现在的值为%ld",ddd);
        };
        ddd = 100;
        block7();
    

    通过(1)说明block只有在调用的时候其内部代码才会执行,不调用其内部代码不会执行,通过(2)和(3)说明block可以访问局部变量,但是不能对其进行修改,如果要在block里面修改局部变量的值,那么要在局部变量前面加_ block修饰符 。通过(4)和(5)可以看出如果局部变量不加 block来进行修饰,那么当在block定义后block调用前修改局部变量的值,那么block里面的局部变量依然是block定义时的值(因为在block定义的时候就将局部变量的值传给了block所指向的结构体,所以局部变量的改变不会影响block里面的局部变量,但是block里面的局部变量也是不可修改的),如果局部变量前面加 _block修饰,那么当在block定以后block执行前对局部变量进行修改,block里面的局部变量值也会进行相应的改变(因为用__block声明局部变量后,在block里面是把局部变量的指针传给block指向的结构体的,所以其值可以被改变)。

    2.2block对全局变量的访问

    (1)// 声明全局变量global
    int global = 100;
    void(^myBlock)() = ^{
       NSLog(@"global = %d", global);
    };// 调用后控制台输出"global = 100"
    myBlock();
    
    (2)// 声明全局变量global
    int global = 100;
    void(^myBlock)() = ^{
     NSLog(@"global = %d", global);// 调用后控制台输出"global = 101"
    };
    global = 101;
    myBlock();
    
    (3)// 声明全局变量global
    int global = 100;
    void(^myBlock)() = ^{
        global ++;
       NSLog(@"global = %d", global);// 调用后控制台输出"global = 101"
    };
    myBlock();
    

    结论:block可以直接对全局变量进行访问和修改(全局变量所占用的内存只有一份,供所有函数共同调用,在Block定义时并未将全局变量的值或者指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的)

    2.3 block对全局静态变量的访问

    (1)// 声明静态变量global
    static int global = 100;
    void(^myBlock)() = ^{
       NSLog(@"global = %d", global);// 调用后控制台输出"global = 100"
    };
    myBlock();
    
    (2)// 声明静态变量global
    static int global = 100;
    void(^myBlock)() = ^{
        NSLog(@"global= %d", global);// 调用后控制台输出"global = 101"
    };
    global = 101;
    myBlock();
    
    (3)// 声明静态变量global
    static int global = 100;
    void(^myBlock)() = ^{
        global ++;
       NSLog(@"global = %d", global);// 调用后控制台输出"global = 101"
    };
    myBlock();
    

    结论:block里面可以直接访问和修改静态全局变量(因为在Block定义时便是将静态变量的指针传给Block变量所指向的结构体,因此在调用Block之前对静态变量进行修改会影响Block内部的值,同时内部的值也是可以修改的)

    3 block的使用方式

    block的使用与代理很类似,代理是让某个类满足自己的某个协议,然后实现协议里面的方法。Block是自己在本类定义了一个代码块,但是在需要的时候才会去调用。下面是一个block使用的小列子:
    业务需求是在一个封装的view中点击了tableView的某一行,然后通过block把这一行上的字符串传出去进行处理,下面是具体实现:

    view的.h文件:
    typedefvoid (^ChooseViewClick)(NSString*);
    @interfaceChooseView : UIView<UITableViewDataSource,UITableViewDelegate>
    @property(nonatomic,copy)ChooseViewClick chooseClick;
    @end
    

    当tableView的某一行被点击时:

    - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        [self dismiss];
        NSString *str =_showArray[indexPath.row];
        if(self.chooseClick) {
           self.chooseClick(str);
        }
     }
    

    在另一个控制器进行处理

     _chooseView.chooseClick = ^(NSString *str)
      {
               _selectTag = 0;
                [btnsetTitle:str forState:UIControlStateNormal];
            };
    

    4 block使用之避免循环引用

    在使用block的时候要特别注意的是避免循环引用,循环引用指的是两个对象都相互强引用了对方,从而导致谁也释放不了,引起内存泄露问题。最常见的分为两种情况,一种是这个对象拥有这个block,但是在block块里面又引用了这个对象本身,第二种是block是宿主的一个属性,但是在block里面又访问了宿主的其他属性。
    第一种情况,block是self本身的一个属性,在block里面又访问了宿主本身:

    self.myblock = ^{
    [self doSomething];
    };
    

    在这里会引起循环引用,解决办法是用__weak修饰,正确代码如下:

    __weak typedef(self) weakSelf = self;
    self.myblock = ^{
    [weakSelf doSomething];
    };
    

    第二种情况,block是宿主本身的属性,在block里面又访问了宿主的其他属性:

    self.myblock = ^{
    [self.muArray removeAllObjects];
    };    //引起循环引用
    

    此时解决方案跟上面一样,可以加__block 或者__weak 来修饰,正确代码如下:

    __block typedef(self) weakSelf = self;
    self.myblock = ^{
    [weakSelf.muArray removeAllObject];
    };
    

    相关文章

      网友评论

        本文标题:block的使用浅析

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