美文网首页
ios中block的基本了解和使用

ios中block的基本了解和使用

作者: Arthur澪 | 来源:发表于2017-12-06 22:21 被阅读0次

    简介

    Block是将函数及其执行上下文封装起来的对象。可以保存一段代码,在需要的时候调用。

    声明

    格式为: 返回值类型(^Block名字)(参数列表);
    如:

    // 无参无返回
    void (^aBlock)(void);
    
    // 无参有返回
    int(^aBlock)(void) ;
    
    // 有参有返回
    NSString* (^aBlock)(NSString * x, int y);
    
    // 无参有返回
    NSString* (^aBlock)(void);
    
    • block属性,使用copy修饰,最好不用strong。
    @property(nonatomic,copy)NSString* (^ablock)(void);
    
    • 自定义Block类型
    typedef void (^Block)(void);
    typedef int (^MyBlock)(int a, int b);
    typedef void(^ConfirmBlock)(BOOL isOK);
    typedef NSString *(^AlertBlock)(NSInteger alertTag);
    

    赋值

    赋值格式为: Block变量名 = ^返回类型(参数列表){ 函数体 };
    如:

    // 无参无返回
    self.aBlock = ^{                 
        NSLog(@"... ...");
    };
    
    // 无参有返回
    self.ablock = ^NSString *{
        NSLog(@"dd");
        return @"dd";
    };
    // 有参有返回
    self.ablock = ^NSString *(int a) {
            
        return @"vv";
    };
    
    // 有参无返回
    self.aBlock = ^(NSString *x, int y){
        NSLog(@"mmm");
    };
    
    

    block由其所属的类对象调用,指明要做什么。block中包含的是一段代码,含义是执行一些操作,具体什么操作,相当于给属性赋值,只是这里给block赋一段代码。

    调用

    由其所属的类对象调用。类似方法调用
    如:

    //无返回
    self.ablock();
    
    //有返回
    NSString *result = self.ablock(2);
    

    应用

    在处理异步问题的时候,例如HTTP请求,有点像javascript的回调,在得到回复的时候更新主线程,而不会占用主线程,比Delegate逻辑好看多了;或者,当你要返回多个值又懒得创建一个类的时候。

    1.传递数据(把数据传递出去、逆传)
    • 在自己的.h中,声明带参数的block属性。
    @interface PDMineVC : PDViewController
    
    @property(nonatomic,copy)void (^rebackBlock)(NSString *flag);
    
    @end
    
    • 在.m中,调用block。
    // 调用,把需要传递的信息 作为参数
    self.rebackBlock(@"425");
    
    • 在外界
      类对象定义自己的block属性。
    PDMineVC *vc = [[PDMineVC alloc] init];
    vc.rebackBlock = ^(NSString *flag) {
        NSLog(@"--%@",flag);
    };
    [self.navigationController pushViewController:vc animated:YES];
    
    2.捕获变量

    有两种方式:值传递、指针传递

    • 值传递的情况
      在声明Block之后、调用Block之前,对局部变量进行修改。调用Block后,局部变量值不变。
    int temp = 100;     
    // 声明Block
    void(^myBlock)() = ^{
        NSLog(@"global = %d", temp);
    };
    
    temp = 101;  // 修改变量的值
    
    // 调用Block
    myBlock();     
    // 调用后输出"temp = 100"
    

    解释:声明Block的时候,只是把变量的保存进Block代码块中,所以调用Block时,打印的仍然是变量原来的值。

    引用block 外的变量,block 默认是将变量的复制到其数据结构中来实现访问的。

    • 指针传递的情况
      对于上一种情况,只需用__block修饰变量即可,只要变量值改变,值就跟着改变。
    __block int temp = 100; 
    

    解释:对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的。

    注意:
    不可以在Block中修改局部变量;
    全局变量、static静态变量,在Block中的值也会随修改而变。

    3.保存一段代码

    在本类中定义Block,本类对象在另一个类中调用Block。

    4.block作为函数的参数

    把block当成方法的参数使用
    在外界写block的声明,在方法内部调用block。
    例子

    方法的声明
    +(void)checkNetworkStatusOn:(void (^)(NSString * status))CallBack;
    
    方法的定义
    +(void)checkNetworkStatusOn:(void (^)(NSString * status))CallBack{
        // ...
        // blcok调用,传递信息
        CallBack ? CallBack(@"网络处于断开状态") : nil;
    }
    
    方法的调用,并声明Block
    [self checkNetworkStatusOn:^(NSString *status) {
    
        NSLog(@"---->%@",status);
    }];
    
    // 定义带参的block类型,本类对象调用,参数来自本类对象
    typedef void(^selectedHandler)(NSUInteger index,NSString *title);
    
    
    // 声明方法,block作为参数
    - (instancetype)initWithFrame:(CGRect)frame
                           titles:(NSArray *)titles
                  selectedHandler:(selectedHandler)handler;
    
    - (instancetype)initWithFrame:(CGRect)frame titles:(NSArray *)titles selectedHandler:(selectedHandler)handler {
        if ( [super initWithFrame:frame]) {
            self.ablock = handler  ;   // 外界传入代码块,外界来决定做什么
        }
        return self;
    }
    
    
    5.block作为函数的返回值(少用)
    
    

    block内存管理

    如果对Block进行一次copy操作,那么Block的内存会被移动到中,这时需要开发人员对其进行内存管理。

    在Block存储在堆中时,如果在Block中引用了外面的对象,会该对象进行一次retain操作。即使在Block自身调用了release操作之后,不会release该对象,这时会造成内存泄漏。

    Person *pObject = [[Person alloc] init];
    
    void(^myBlock)() = ^{
        NSLog(@"------%@", pObject);
    };
    myBlock();
    
    Block_copy(myBlock);    // copy操作
    
    // ...
    
    Block_release(myBlock);
    
    [pObject release]; 
    // -->Person对象在这里无法正常被释放,因为其在Block中被进行了一次retain操作。
    
    

    为了不对所引用的对象进行一次retain操作,可以使用__block来修饰对象

    __block Person *pObject = [[Person alloc] init];
    

    block循环引用

    如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用。
    解决:

    • __block修饰
      在对象的前面使用__block来修饰,以避免Block对对象进行retain操作。
      __block Person *p = [[Person alloc] init];

    • 弱指针
      新创建一个弱指针来指向该对象,并将该弱指针放在Block中使用,这样Block便不会造成循环引用。

    __weak typeof(Person) weakP = p;
    
    void(^myBlock)() = ^{
        NSLog(@"------%@", weakP);
    };
    
    

    这样虽然解决了循环引用,但是也容易涉及到另一个问题:
    因为Block是通过弱引用指向了Person对象,那么有可能在调用Block之前,Person对象已经被释放。所以我们需要在Block内部再定义一个强指针来指向Person对象。

    __weak typeof(Person) wp = p;
        aBlock = ^(NSString *flag) {
            
            __strong typeof(Person) sp = wp;
            
            NSLog(@"%@", sp);
        };
    

    补充:
    在ARC默认情况下,Block的内存存储在堆中,ARC会自动进行内存管理,程序员只需要避免循环引用即可。
    在Block内部定义的变量,会在作用域结束时自动释放,Block对其并没有强引用关系,且在ARC中只需要避免循环引用即可,如果只是Block单方面地对外部变量进行强引用,并不会造成内存泄漏。

    相关资料

    http://www.jianshu.com/p/14efa33b3562 *
    http://www.jianshu.com/p/649fca982c6e
    http://www.jianshu.com/p/45557fbef2bf
    https://www.jianshu.com/p/c389afedbce6 *

    相关文章

      网友评论

          本文标题:ios中block的基本了解和使用

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