Block的使用及循环引用的解决

  Block是一个很好用的东西,这篇文章主要来介绍:1.什么是Block?2.Block的使用?3.Block的循环引用问题及解决.

1.什么是Block?

  说这个问题之前,我先来说一下闭包(Closure)。闭包就是一个函数,或者一个指向函数的指针,加上这个函数执行的非局部变量。说的通俗一点,就是闭包允许一个函数访问声明该函数运行上下文中的变量,甚至可以访问不同运行上文中的变量。

  Block实际上就是OC语言对闭包的实现。

  下面声明一个Block:

 

1 int (^Sum)(int, int);

 

  首先,第一个int也就是该Block的返回值类型,也可以是其他的返回值类型,或者无返回值。“^”是Block的标识。第二以及第三个int,则是Block的参数类型,当然也可以无参数。

  下面的代码则是定义了一个Block,当执行Line5代码时,会输出“nihao”,也就是给Block传入的参数。

1 void (^printBlock)(NSString *) = ^(NSString *str) {
2     NSLog(@"%@", str);
3 };
4 
5 printBlock(@"nihao");

下面借鉴一张网上流传的图片总结一下Block的结构:

  需要说明的是,Block能够直接修改和访问全局变量,但是只能访问局部变量,如果想要在Block中修改局部变量,使用 __block 修饰即可。关于Block的基本介绍就是这么多了。有需要的tx可以留言,再做适当的更新。下面简单说说Block的使用。

2.Block的使用

2.1 使用Block在不同的页面进行传值

  页面传值有很多方式,比如:属性、单例、通知、代理,Block等。这里单说使用Block传值,其他几种以后再做补充。

  使用Block传值也有两种方式:Block属性和使用Block作为参数的方法。

2.1.1 使用Block属性传值

  下面演示的是从B页面(后一个页面)传值到A页面(前一个页面)。

  首先在B页面中声明一个Block,通常为了使代码简洁,我一般会对Block进行重定义:

typedef void (^PassVal)(NSString *text);

  接着,在B中声明一个Block属性:

@property (nonatomic, copy) PassVal passVal;

  这里需要注意下,我使用了copy,实际上在ARC环境下,使用copy和strong都是可行的。我这里只是为了表明,使用Block时,实际上是把Block从栈区拷贝到了堆区。

  下面继续。我这里选择在B页面即将消失时,将值传出:

1 - (void)viewWillDisappear:(BOOL)animated {
2     if (self.passVal) {
3         self.passVal(@"1234567898");
4     }
5 }

  我们可以看一下前面声明的Block是一个有参(string类型),无返回值。这里我给Block的参数是@"123456798"。通过上面的处理,B页面的操作已经完成了。下面进行A页面的操作:

- (void)btnNextAction:(UIButton *)btnNext {
    BViewController *BVC = [[BViewController alloc] init];
    BVC.passVal = ^(NSString *text) {
        NSLog(@"%@",text);
    };
    [self.navigationController pushViewController:BVC animated:YES];
}

  上面的代码演示了,在点击按钮即将push到B页面时,来实现Block。这样就可以在A页面中得到B页面的值,也就是text值。到此,Block属性传值结束。

2.1.2 使用带有Block参数的方法传值

  这种方式和属性传值类似,下面做简单的示例:

1 typedef void (^PassVal)(NSString *text);
2 
3 @interface SecondViewController : UIViewController
4 
5 @property (nonatomic, copy) PassVal passVal;
6 
7 - (void)returnText:(PassVal)block;
8 
9 @end

  首先在第二个页面同样需要声明Block属性,同时在第二个页面声明一个方法,方法的参数即是属性同样类型的Block。接着实现下面的方法:

1 - (void)viewWillDisappear:(BOOL)animated {
2     if (self.passVal) {
3         self.passVal(@"1234567898");
4     }
5 }
6 
7 - (void)returnText:(PassVal)block {
8     self.passVal = block;
9 }  

  首先要做的还是先给Block的参数赋值,接着要实现声明的方法。接下来去第一个页面操作:

1 - (void)btnNextAction:(UIButton *)btnNext {
2     SecondViewController *secCtr = [[SecondViewController alloc] init];
3     [secCtr returnText:^(NSString *text) {
4         NSLog(@"%@",text);
5     }];
6     
7     [self.navigationController pushViewController:secCtr animated:YES];
8 }

  这样也可以实现传值。

  使用方法传值,猛一看觉得挺麻烦,但是实际上,使用方法调用的方式是很容易理解的。

  传值只是Block的简单应用,总体来说,Block和代理的使用时很类似的,可以互相参考着学习。

3.Block的循环引用问题及解决.

  首先我们需要明确的是,一个对象的Block属性是使用copy来修饰,当Block被copy时,会对block中用到的对象产生强引用(ARC)或者引用计数加一(MRC)。当我们使用Block时,如果Block方法又引用了对象,如使用 self. 来引用对象的属性,这就会造成循环引用。其实如果产生了循环引用我们也不需要很担心,因为编译器会自动提醒,只需要在提醒的时候进行处理就可以了.一般在ARC下可以这么处理(仅作为示例用法):

    __weak typeof(self)weakSelf = self;
    [self.tableView addHeaderWithCallback:^{
        weakSelf.isDown = YES;
        weakSelf.page = 1;
        [weakSelf requestData];
    }];

也就是说,把需要使用self的地方换成weakSelf即可.如果是MRC,只需要把上面代码的第一行更换为:

__block typeof(self)weakSelf = self;

以上仅作为各位使用的参考,如果有不到位的地方,欢迎各位留言指出,大家一起交流.

posted on 2016-04-25 13:40  夏夏天阳  阅读(1485)  评论(0编辑  收藏  举报

导航