美文网首页iOS开发
谈谈Objective-C中block块

谈谈Objective-C中block块

作者: kamto | 来源:发表于2017-08-01 18:59 被阅读18次

    在Objective-C中block可以当做匿名函数,其本质和只读属性的变量很相似,使用block,就可以像其他标准函数一样,传入参数,并得到返回值,也可以传递回调函数。

    标准格式:

    a(^b)(c)=^(d){
       
    };
    
    a: 返回值类型,可以是对象或者基本类型,也可以是无返回值的void
    ^: 块的语法标记,声明b为一个block对象
    b: block对象名称
    c: 实参列表,可以是对象或者基本类型,也可以是block对象,也可以是void
    d: 形参列表,和实参列表对应
    

    例如:

     int (^exmpleBlock)(int a, int b)=^(int a ,int b){
         return  a + b;
     };
    int result = exmpleBlock(1,2);
    

    block的常见类型

    1.无返回值、无参数的block

    void (^exmpleBlock)()=^(){
        NSLog(@"exmpleBlock");
    };
    exmpleBlock();
    

    定义block的括号可以省去,如

    void (^exmpleBlock)()=^{
        NSlog(@"exmpleBlock");
     };
    exmpleBlock();
    

    使用 dispatch_block_t 定义的block是一个无返回值无参数的Block

    dispatch_block_t exmpleBlock = ^{
        NSlog(@"exmpleBlock");
    };
    exmpleBlock();
    
    1. 无返回值、有参数的block
    void (^exmpleBlock)(int a,int b)=^(int a,int b){
        NSLog(@"sum: %d",a+b);
    };
    exmpleBlock(1,2);
    
    1. 有返回值、有参数的block
    int (^exmpleBlock)(int a,int b)=^(int a,int b){
         return  a +b;
     };
     int  result =  exmpleBlock(1,2);
    

    4.有返回值、无参数的block。形式上可以存在,但基本不会这样用,就相当于直接赋值,何必多次一举呢

     int (^exmpleBlock)()=^{
          return 1;
      };
     int  result =  exmpleBlock();
    

    block内部不能修改外部的变量,除非使用__block修饰,如:

     __block int sum = 0;
     void (^exmpleBlock)(int a, int b)=^(int a, int b){
          sum = a+ b;
      };
      exmpleBlock(1,2);
      NSLog(@"sum: %d”,sum);
    

    前文说到block可以看作一个对象,也可以作为对象的一个属性。

    例子1 :
    在对象Person中定义Block属性:

    typedef void(^exmpleBlock)(NSString *msg);
    
    @interface Person : NSObject
    
    @property (nonatomic, copy) exmpleBlock block;
    

    也可以这样

    @property (nonatomic, copy) void(^exmpleBlock)(NSString *msg);
    
    初始化:
    Person *person = [[Person alloc] init];
    person.block = ^(NSString *ms){     
      NSLog(@“%@”,msg);
    }
    
    使用:
    Person.block(@“Test”);
    

    例子2:

    - (void)exmple:(int)a Block1:(void(^)(NSString *msg))block1 Block2:(void(^)())block2{
        if (a == 1) {
            block1(@"1");
        }else{
            block2();
        }
        
    }
    
    [self exmple:1 Block1:^(NSString *msg) {
            NSLog(@"传递的参数是%@",msg);
        } Block2:^{
            NSLog(@"传递的参数是2");
      }];
    

    循环引用

    对象A持有对象B,对象B持有对象A,相互持有,这样两者的retainCount值一直都无法为0,于是内存始终无法释放,导致内存泄露。

    block中循环引用问题

    由于block会对block中的对象进行持有操作,就相当于持有了其中的对象,而如果此时block中的对象又持有了该block,则会造成循环引用。

    例如:

    @interface Person  ()
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) void (^exmpleBlock)();
    @end
    
    Person *person = [[Person alloc] init];
    person.name = @"joe";
       person.exmpleBlock = ^{
       NSLog(@"%@",person.name);
     };
    

    此时必然提出警告:Capturing ’person’ strongly in this block is likely to lead to a retain cycle

    只要block中用到了对象的属性或者函数方法,block就会持有该对象,如例子中person强引用了block,block在回调时又强引用了person。

    解决方案:

      __weak Person *weakPerson = person;
    //也可以这么写  __weak typeof(person) weakPerson = person;
     person.exmpleBlock = ^{
         NSLog(@"%@",weakPerson.name);
     };
    

    weak引用的对象如果被释放了,那么对应的weak对象就会被指为nil

    当然不要一看到block就考虑用__weak 修饰。

    当 block 本身不被 self 持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用 weak self 了。

    比如一些不会造成循坏引用的例子。

    dispatch_async(dispatch_get_main_queue(), ^{
        [self doSomething];
    });
    

    self并没有对GCD的block进行持有,所以不会造成循环引用。

    - (void)test:(void(^)())block{
        block();
    }
    
     [self test:^{
        NSLog(@"%@",self.person);
      }];
    

    这个并不会造成循环引用,此block只是一个临时变量,并不会对self造成引用。

    3、

    [UIView animateWithDuration:0.2 animations:^{
        self.alpha = 1;
    }];
    

    UIView 的某个负责动画的对象持有了 block,block 持有了 self。但因为 self 并不持有 block,所以就没有循环引用产生,因为就不需要使用 weakSelf。

    那我们什么时候用到strongSelf呢

    传进 block 之前,把 ‘self’ 转换成 weak automatic 的变量,这样在 block 中就不会出现对 self 的强引用。如果在 block 执行完成之前,self 被释放了,weakSelf 也会变为 nil。
    在 block 中先写一个 strong self,其实是为了避免在 block 的执行过程中,突然出现 self 被释放的尴尬情况。通常情况下,如果不这么做的话,还是很容易出现一些奇怪的逻辑,甚至闪退。__strong确保在 block 内,strongSelf 不会被释放。
    strongSelf 是一个局部变量,存在栈中,执行完这个block,strongSelf 就会自动释放。

    当然我们也会看到strongSelf在block里的使用。

    下面会用一个例子来说明:

     __weak typeof(person) weakPerson = person ;
     person.exmpleBlock = ^{
          NSLog(@"%@",weakPerson.name);
          [weakPerson text];
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@“name is = %@",weakPerson.name);
          });
      };
     person.exmpleBlock();
    

    打印:name is = (null)

    原因:exmpleBlock结束之后,person会被释放,在原对象释放之后,__weak对象就会变成null,
    又由于dispatch_after里面捕获的weak的student,所以为防止野指针。所以就输出了null了

    我们可以修改成这样:

    __weak typeof(person) weakPerson = person ;
    person.exmpleBlock = ^{
         NSLog(@"%@",weakPerson.name);
         [weakPerson text];
         __strong typeof(weakPerson) strongPerson = weakPerson;
         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
             NSLog(@“name is = %@",weakPerson.name);
          });
        };
    person.exmpleBlock();
    

    weakSelf 是为了block不持有self,避免Retain Circle循环引用。在block内如果需要访问self的方法、变量,建议使用 weakSelf。

    strongSelf的目的是因为一旦进入block执行,假设不允许self在这个执行过程中释放或者在block内需要多次访问self,就需要使用strongSelf。

    总结:

    1 在 block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
    2 如果在 block 内需要多次访问 self,则需要使用 strongSelf。

    相关文章

      网友评论

        本文标题:谈谈Objective-C中block块

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