iOS开发之手把手教你使用Block

150 阅读3分钟

配图.jpg
说到iOS面试block可能是必问的问题了,因为它在我们现实的开发中太常用了,而且个人觉得很好用,无论是界面传值还是当做参数都十分的灵活。像GCD、AFNetwork的回调等都有block的影子,那接下来我们来研究下block的一些使用吧,文章将会从block的简单用法、利用block在界面传值、block如何避免循环引用三个模块来讲解。


###1、block的简单用法

  • 无参无返回的block
void(^WBBlock1)()=^(){
       NSLog(@"WBBlock1");
 };
 WBBlock1();
  • 有参有返回的block
int (^WBBlock2)(int)=^(int num){
      return num;
 };
 WBBlock2(2);
  • 有参无返回的block
 void (^WBBlock3)(int)=^(int num){
      NSLog(@"%d",num);
 };
 WBBlock3(3);

###2、利用block在界面传值 我们接下来通过一个WBNavagationView的类来传递点击事件 下面代码是在主页面MainViewController调用WBNavagationView实例来传递点击事件

_navagationView=[[WBNavagationView alloc]init];
__weak MainViewController *weakSelf = self;
[_navagationView addLeftClick:^(int num){//第一步:主界面定义需要被回调的代码块
         NSLog(@"%d",num);
 }];

WBNavagationView.m文件中的代码

typedef void (^BtnBlock)(int num);//第二步:定义BtnBlock类型的block块

@interface WBNavagationView: UIView
{    
    BtnBlock _buttonBlock;//第三步:BtnBlock定义相应的代码块
}
@property(strong,readonly,nonatomic)UIButton *button;
-(void)addLeftClick:(BtnBlock)btnBlock;//第四步:将第一步主界面定义代码块赋值给第三步定义代码块
@end

WBNavagationView.h文件中的具体实现block传值代码

- (instancetype)init
{
    self = [super init];
    if (self) {
//定义button用来出发第一步中主界面MainViewController中定义的代码块
button =[[UIButton alloc]initWithFrame:CGRectMake(100, 100, 60, 30)];
[button addTarget:self action:@selector(BtnClick:) forControlEvents:UIControlEventTouchUpInside];
        _leftButton.tag=0;
  [self addSubview: button];
    }
   return self;
}
-(void)addLeftClick:(BtnBlock)btnBlock
{
    _ btnBlock = btnBlock;//第四步:接收第一步主界面传递的点击事件代码块
}

-(void)BtnClick:(UIButton *)btn
{
     _btnBlock(6);//第五步:点击button回调第一步主界面定义代码块
}

大概过程就是MainViewController中定义代码块传递给WBNavagationView,WBNavagationView接收代码块后适当的时候回调主界面代码块将值传递过去。


###3、block如何避免循环引用 Blocks可以访问局部变量,但是不能修改 如果修改局部变量,需要加__block

__block int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
      multiplier ++;//这样就可以了
      return num * multiplier;
};

如果局部变量是数组或者指针的时候只复制这个指针,两个指针指向同一个地址,block只修改指针上的内容。如:

NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@"a",@"b",@"abc",nil];
    NSMutableArray *mArrayCount = [NSMutableArray arrayWithCapacity:1];
    [mArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock: ^(id obj,NSUInteger idx, BOOL *stop){
        [mArrayCount addObject:[NSNumber numberWithInt:[obj length]]];
}];
   
NSLog(@"%@",mArrayCount);

例子里面确实没有修改mArrayCount这个局部变量啊。mArrayCount是一个指针,指向一个可变长度的数组。在block里面,并没有修改这个指针,而是修改了这个指针指向的数组。换句话说,mArrayCount是一个整数,保存的是一块内存区域的地址,在block里,并没有改变这个地址,而是读取出这个地址,然后去操作这块地址空间的内容。 这是允许的,因为声明block的时候实际上是把当时的临时变量又复制了一份,在block里即使修改了这些复制的变量,也不影响外面的原始变量。即所谓的闭包。 但是当变量是一个指针的时候,block里只是复制了一份这个指针,两个指针指向同一个地址。所以,在block里面对指针指向内容做的修改,在block外面也一样生效。

_weak _typeof(&*self)weakSelf =self; 等同于_weak UIViewController *weakSelf =self;

总结: 1._block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。 2._weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。 3._block对象可以在block中被重新赋值,_weak不可以。 PS:_unsafe_unretained修饰符可以被视为iOS SDK 4.3以前版本的_weak的替代品,不过不会被自动置空为nil。所以尽可能不要使用这个修饰符。

结语:小伙伴们觉得还行就给点个赞吧^_^