iOS之基础知识IOSiOS开发

iOS篇-block篇-0基础到熟练应用全面解析

2017-06-26  本文已影响423人  TianTianBaby223

写在前面:demo 或者代码 评论发邮箱地址,我会及时给需要的小伙伴发过去.

一 : 科普一分钟

既然分析明白了就可以做事情了,接下来进入block 的神器世界

二 : block的基本使用

  1. 第一种 :没有参数 没有返回值 = 右边相当对名字为 doit1这个block对象的代码块的赋值 里面存放一段代码
void(^doit1)() = ^(){
       NSLog(@"关注我吧,会有更多精彩");
};
void(^block2)() = ^{
  NSLog(@"关注我吧,会有更多精彩");
    };

如果有参数,定义的时候 必须要写参数 而且必须要有参数变量名

void(^block22)(int) = ^(int a){

      NSLog(@"关注我吧,会有更多精彩");

    };
int(^block3)() = ^int{
    
        return 3;
        
    };

也可以写成

 int(^block3)() = ^{
     
     return 3;
     
     };
 int(^doit4)(NSString *) = ^(NSString *name){
    
        return 2;
        
    };
 doit1();

之前我们已经声明了 doit1 这个block 代码块对象 所以运行后的结果是
NSLog(@"关注我吧,会有更多精彩");

 <#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
     <#statements#>
     };

例如
@property(nonatomic,strong) void(^doit)() ;
一个无返回值,无参数的block类型 名字为doit的属性对象

当然这样书写 我们会不习惯 我们还可以重新定义类型 来写成我们熟悉的形式.
例如重新声明类型

//BlockType;类型的别名
typedef void(^BlockType)();

注意 这个BlockType 是类型名 并不是对象名
所以我们要再定义一个 类型 为BlockType 名字为doit 的对象
@property(nonatomic,strong) BlockType doit;

- blcok 内部变量传递分析
- (void)viewDidLoad {
    [super viewDidLoad];
    int a = 3;
    
    a = 5;
    
    void(^block)() = ^{
    
        NSLog(@"-- %d",a);

    };
    block();
    
    
}

结果为 : 5

- (void)viewDidLoad {
    [super viewDidLoad];
    int a = 3;
    
   
    
    void(^block)() = ^{
    
        NSLog(@"-- %d",a);
        
    
    };
    
     a = 5;
    block();
    
    
}

结果为 : 3

- (void)viewDidLoad {
    [super viewDidLoad];
  static  int a = 3;
    void(^block)() = ^{
    
        NSLog(@"-- %d",a);
    };
    
     a = 5;
    block();  
}

三 : block 在开发中的基本应用

1. block 保存一段代码

实例场景:在我们写tableView列表中 根据不同的cell 判断不同点击事件 我们多数人的做法是 通过很多很麻烦的判断如下:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    if (indexPath.row == 0) {
        
    }else if (indexPath.row == 1){
   
    }else if (indexPath.row == 2){
   
    }
   
}

其实我们可以用Block 存储要做的事情 来代替这些复杂的判断,因为这样增加了代码的可读性,而且需求发生变化的时候也非常容易的完成了需求的变化
做法:

//保存每个cell 做的事情
@property(nonatomic,strong) void(^block)();
  cellItem *item1 = [cellItem itemWithTitle:@"打电话"];
    item1.block = ^{
        NSLog(@"睡觉");
    };
    
    cellItem *item2 = [cellItem itemWithTitle:@"发短信"];
    item2.block = ^{
        NSLog(@"吃饭");
    };
    
    cellItem *item3 = [cellItem itemWithTitle:@"发邮件"];
    item3.block = ^{
        NSLog(@"装逼");
    };
    _items = @[item1,item2,item3];
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    cellItem *item = self.items[indexPath.row];
    if (item.block) {
        item.block();
    }
    
}
2. 逆向传值

我们的经典逆向传值的例子就是代理了吧,大多数的开发者也是习惯于代理的方式,但是block 的传值方式要比代理好太多了,代理的6步骤比较复杂. 接下来我们要用block 替换代理
实例场景:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{


    TZpopViewController *vc  = [[TZpopViewController alloc]init];
    vc.block = ^(NSString *index) {
        
        NSLog(@"index = %@",index);
        
    };
    
    vc.view.backgroundColor = [UIColor brownColor];
    [self presentViewController:vc animated:YES completion:nil];
    

}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (_block) {
        _block(@"tz123");
        
    }

}
= ^(NSString *index) {

        NSLog(@"index = %@",index);

    };

此时这个代码片段是不走的 ,因为没有操作执行block

当我们触发TZpopViewController 中的 touchesBegan方法时候 做了执行block 的方法

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (_block) {
        _block(@"tz123");
        
    }

}

此时则会在ViewController 中走打印方法 打印结果为tz123

四 : block 内存管理

1.在MRC 中block只能使用copy 修饰,不能使用retain,因为使用retain 了block 存放于栈区,被销毁了.

2.在MRC只要block引用了外部局部变量,block 放在栈里面,只要block 没有引用外部局部变量,block 放在全局区里面.

1.只要block 引用外部局部变量,block放在堆里面

注意:在ARC block 使用 strong 修饰,最好不用使用 copy 节省性能.

有的小伙伴会反驳说用copy ,其实是看需求而定 ,因为我们大部分需求是不可变赋值给不可变 ,所以这里建议用strong .如果保守的话也可以用copy

五 : block 的循环引用问题

- (void)viewDidLoad {
    [super viewDidLoad];

    _block1 = ^{

     NSLog(@"关注我有更多精彩%@",self);

         };
    
    _block1(); 
}

这种写法就会造成循环引用.

    __weak typeof (self) weakself = self;

 _block1 = ^{

          
        NSLog(@"关注我有更多精彩%@",weakself);

    };
    
    _block1();
}
解决循环引用图解.png
- (void)viewDidLoad {
    [super viewDidLoad];

    __weak typeof (self) weakself = self;
    
    _block1 = ^{

        __strong typeof (weakself) strongself = weakself;
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"关注我有更多精彩%@",strongself);

        });
    
    };
    
    _block1();
    
    
}

再次强引用一次,知道 延迟代码走完,则指针释放 .

六 : block 在开发中的高级应用

- block 当参数在开发中的应用

1.什么情况下block 被当成参数了呢,
看参数有没有^ 如果有^ 怎参数为block.
2.什么时候需要把block 当成参数,
做的事情由外部决定,什么时候做由内部决定.

代码举例:

TZcaculoterManager

@property(nonatomic,assign)NSInteger result;
//计算
-(void)cacutor:(NSInteger(^)(NSInteger result))cacultorblock;

-(void)cacutor:(NSInteger(^)(NSInteger result))cacultorblock{


   _result = cacultorblock(_result);
    
}

调用

- (void)viewDidLoad {
    [super viewDidLoad];
    caculoterManager *magr = [[caculoterManager alloc]init];
    
    [magr cacutor:^(NSInteger result){
        
        result += 5;
        result += 7;
        return result;
        
    }];

}
-(void)magrCautor:(NSString*)tz{

}

感觉是一个意思,后者传递的是字符串,前者传递的是block 代码块,区别在于前者控制 传递的代码块何时调用.AFN 封装 等各种封装 很多采用这种

- block 当返回值在开发中的应用
-(void(^)())test{


    return ^{
    
    
    };
    
}

这个是一个返回值 为无返回值,无参数的block
现在我们来调用这个函数

self.test();
相当于 self.test 这个函数给我们返回block
然后我们去执行 block()

cacutotermanager

.h

@property(nonatomic,assign)int result;

-(cacutotermanager* (^)(int))add;

.m

-(cacutotermanager* (^)(int))add{

    return ^(int value){
    
        _result += value;
        
        return self;
        
    };
    
}

调用

  cacutotermanager *magr1 = [[cacutotermanager alloc]init];
   
    magr1.add(1).add(2).add(4);

模仿了 Masonry的链式编程方法.

七 : 总结

通过上述大家对block 的应用应该了解的非常透彻了,希望大家活学活用. 根据需求选择正确的方法.效率更高. ^ _ ^ 下期再见. 想听什么可以在评论区留言. H5,Java 陆续会开始. 加油!!

上一篇 下一篇

猜你喜欢

热点阅读