美文网首页
iOS Block用法

iOS Block用法

作者: Asserts | 来源:发表于2016-06-16 10:34 被阅读0次

    ios4.0系统已开始支持block,在编程过程中,block被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行。Block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:block是inline的,并且它对局部变量是只读的。

    1、block的定义

    // 声明和实现写在一起,就像变量的声明实现 int a = 10;

    int(^aBlock)(int,int) = ^(int num1,int num2) {

    return num1 * num2;

    };

     // 声明和实现分开,就像变量先声明后实现

    int a;

    a = 10;

     int(^cBlock)(int,int);

     cBlock = ^(int num1,int num2)

     {

     return num1 * num2;

     };

    其中,定义了一个名字为aBlock的block对象,并携带了相关信息:

    1、aBlock 有两个形式参数,分别为int类型;

    2、aBlock 的返回值为int 类型;

    3、等式右边就是block的具体实现;

    4、^ 带边block声明和实现的标示(关键字);

    当然,你可以定义其他形式的block。e.g:无返回值,无形式参数等;

    void(^bBlock)() = ^()

    {

    int a =10;

    printf(num = %d,a);

    };


    2、block 访问权限

    block可以访问局部变量,但是不能修改。

    int a =10;

    int(^dBlock)(int) = ^(int num)

    {

    a++;//not work!

    return num * a;

    };

    此处不能修改的原因是在编译期间确定的,编译器编译的时候把a的值复制到block作为一个新变量(假设是a'= 10),此时a'和a是没有关系的。

    这个地方就是函数中的值传递。如果要修改就要加关键字:__block或者static

    __block int a =7;

    int(^dBlock)(int) = ^(int num)

    {

    a++;// work!

    return num * a;

    };


    3、block的调用

    block调用就像调用函数一样。

    int c = aBlock(10,10);

    bBlock();


    4、block 的应用

    假设我们熟悉代理递值的话,对代理我们可能又爱有恨!我们先建立模型A页面 push B页面,如果把A页面的值传递到B页面,属性和单例传值可以搞定!但是如果Pop过程中把B页面的值传递到A页面,那就可以用单例或者代理了!说到代理,我们要先声明协议,创建代理,很是麻烦。常常我们传递一个数值需要在两个页面间写很多代码,这些代码改变页面的整体顺序,可读性也打了折扣。所以,此时,block是一种优化方案!

    block传值得例子

    一、在第二个视图控制器的.h文件中定义声明Block属性

    typedef void (^ReturnTextBlock)(NSString *showText);

    @interface TextFieldViewController : UIViewController

    @property (nonatomic, copy) ReturnTextBlock returnTextBlock;

    - (void)returnText:(ReturnTextBlock)block;

    @end

    第一行代码是为要声明的Block重新定义了一个名字

    ReturnTextBlock

    这样,下面在使用的时候就会很方便。

    第三行是定义的一个Block属性

    第四行是一个在第一个界面传进来一个Block语句块的函数,不用也可以,不过加上会减少代码的书写量

    二、实现第二个视图控制器的方法

    - (void)returnText:(ReturnTextBlock)block 

    {

    self.returnTextBlock = block;

    }

     - (void)viewWillDisappear:(BOOL)animated 

     {

     if(self.returnTextBlock !=nil) 

     {

     self.returnTextBlock(self.inputTF.text);

     }

    }

    其中inputTF是视图中的UITextField。

    第一个方法就是定义的那个方法,把传进来的Block语句块保存到本类的实例变量returnTextBlock(.h中定义的属性)中,然后寻找一个时机调用,而这个时机就是上面说到的,当视图将要消失的时候,需要重写:

    - (void)viewWillDisappear:(BOOL)animated;

    三、在第一个视图中获得第二个视图控制器,并且用第二个视图控制器来调用定义的属性

    如下方法中书写:

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

    {

    // Get the new view controller using [segue destinationViewController].

    // Pass the selected object to the new view controller.

    TextFieldViewController *tfVC = segue.destinationViewController;

    [tfVC returnText:^(NSString *showText) {

    self.showLabel.text = showText;

    }];

    }

    可以看到代码中的注释,系统告诉我们可以用[segue destinationViewController]来获得新的视图控制器,也就是我们说的第二个视图控制器。

    这时候上面(第一步中)定义的那个方法起作用了,如果你写一个[tfVC returnText按回车,系统会自动提示出来一个:

    tfVC returnText:<#^(NSString *showText)block#>

    的东西,我们只要在焦点上回车,就可以快速创建一个代码块了,大家可以试试。这在写代码的时候是非常方便的。


    5、 block的内存管理

    block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作用域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。比如下面的例子。 我在view did load中创建了一个block:

    - (void)viewDidLoad

    {

    [super viewDidLoad];

    int number = 1;

    _block = ^(){

    NSLog(@number: %d, number);

    };

    }

    并且在一个按钮的事件中调用了这个block:

    - (IBAction)testDidClick:(id)sender {

    _block();

    }

    此时我按了按钮之后就会导致程序崩溃,解决这个问题的方法就是在创建完block的时候需要调用copy的方法。copy会把block从栈上移动到堆上,那么就可以在其他地方使用这个block了~ 修改代码如下:

    _block = ^(){

    NSLog(@number %d, number);

    };

    _block = [_block copy];

    同理,特别需要注意的地方就是在把block放到集合类当中去的时候,如果直接把生成的block放入到集合类中,是无法在其他地方使用block,必须要对block进行copy。不过代码看上去相对奇怪一些:

    [array addObject:[[^{

    NSLog(@hello!);

    } copy] autorelease]];


    6、循环引用

    对于非ARC下, 为了防止循环引用, 我们使用__block来修饰在Block中使用的对象:

    对于ARC下, 为了防止循环引用, 我们使用__weak来修饰在Block中使用的对象。原理就是:ARC中,Block中如果引用了__strong修饰符的自动变量,则相当于Block对该变量的引用计数+1。

    这一点其实是在第一点的一个小的衍生。当在block内部使用成员变量的时候,比如

    @interface ViewController : UIViewController

    {

    NSString *_string;

    }

    @end

    在block创建中:

    _block = ^(){

    NSLog(@string %@, _string);

    };

    这里的_string相当于是self->_string;那么block是会对内部的对象进行一次retain。也就是说,self会被retain一次。当self释放的时候,需要block释放后才会对self进行释放,但是block的释放又需要等self的dealloc中才会释放。如此一来变形成了循环引用,导致内存泄露。

    修改方案是新建一个__block scope的局部变量,并把self赋值给它,而在block内部则使用这个局部变量来进行取值。因为__block标记的变量是不会被自动retain的。

    __block ViewController *controller = self;

    _block = ^(){

    NSLog(@string %@, controller->_string);

    };

    相关文章

      网友评论

          本文标题:iOS Block用法

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