iOS开发

谈谈Objective-C中block块

2017-08-01  本文已影响18人  kamto

在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。

上一篇下一篇

猜你喜欢

热点阅读