iOS -- block

梦想与她 提交于 2020-02-15 10:42:54

 Block:一种匿名函数,可以捕获周围的变量

Block是一种匿名函数,Block中可以保存一段代码,它可以作为参数、作为返回值,在需要的时候调用。常用于GCD、动画、各种回调


block 在实现时就会对它引用到的它所在方法中定义的局部变量进行一次只读(const)拷贝,然后在 block 块内使用该只读拷贝

NSString * functionName = @"functionName";
    
    NSString * (^testBlock)(NSString *) = ^NSString * (NSString * name){
        
        myName = @"nana";
//        functionName = @"fff";    //报错,不能改变自动变量的值
        self.vcName = @"vvvvv";  //没有报错,可以改变变量的值
        
        NSLog(@"%@",name);
        return name;
    };
    testBlock(@"cc");

 如果想要在block里改变局部变量的值,可以用__block修饰符

__block int val = 10;  
void (^blk)(void) = ^{printf("val=%d\n",val);};  
val = 2;  
blk(); 

 

在Block定义时便是将局部变量的值传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改并不会影响Block内部的值,同时内部的值也是不可修改的

在局部变量前使用__block修饰,在Block定义时便是将局部变量的指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的

全局变量所占用的内存只有一份,供所有函数共同调用,在Block定义时并未将全局变量的值或者指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的

在Block定义时便是将静态变量的指针传给Block变量所指向的结构体,因此在调用Block之前对静态变量进行修改会影响Block内部的值,同时内部的值也是可以修改的

在ARC默认情况下,Block的内存存储在堆中,ARC会自动进行内存管理,程序员只需要避免循环引用即可

 

 

block的语法格式

    //有返回值有蚕食的block  NSString * (^testBlock)(NSString *) = ^NSString * (NSString * name){        
        NSLog(@"%@",name);
        return name;
    };
    testBlock(@"cc");
    
    //没有返回值的block
    void (^testBlock)(NSString *) = ^(NSString * name){
        NSLog(@"%@",name);
    };
    testBlock(@"cc");
    
    //没有返回值也没有参数的block
    void (^testBlock)(void) = ^{
        NSLog(@"nonono");
    };
    testBlock();

 

typedef简化Block的声明

     使用typedef简化block声明,然后把block当作方法的参数

     在使用方法的时候传入一个定义好的block

     在方法执行的时候就会执行这个block

typedef void (^blockName)(NSString * name);
[self blockDefine:^(NSString *name) {
     NSLog(@"%@",name);
            
}];
-(void)blockDefine:(blockName)b{    //其他代码
    b(@"bb");
}


 

声明一个block类型的属性

//block属性
@property (nonatomic, copy) void (^btnClickedBlock)(UIButton *sender);//使用typedef定义之后再声明成属性
typedef void(^ClickBlock)(NSInteger index);
@property (nonatomic, copy) ClickBlock imageClickBlock;

 

block的循环引用

 oc中,可能引起循环引用的情况有三种:block,delegate,NStimer

一般来说我们总会在设置Block之后,在合适的时间回调Block,而不希望回调Block的时候Block已经被释放了,所以我们需要对Block进行copy,copy到堆中,以便后用。

Block可能会导致循环引用问题,因为block在拷贝到堆上的时候,会retain其引用的外部变量,如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用

- (void) dealloc {
    NSLog(@"no cycle retain");
} 

- (id) init {
    self = [super init];
    if (self) {

        #if TestCycleRetainCase1
        //会循环引用
        self.myblock = ^{
            [self doSomething];
        };
  
        #elif TestCycleRetainCase2
        //会循环引用
        __block TestCycleRetain * weakSelf = self;
        self.myblock = ^{
            [weakSelf doSomething];
        };

        #elif TestCycleRetainCase3
        //不会循环引用
        __weak TestCycleRetain * weakSelf = self;
        self.myblock = ^{
            [weakSelf doSomething];
        };

        #elif TestCycleRetainCase4
        //不会循环引用
        __unsafe_unretained TestCycleRetain * weakSelf = self;
        self.myblock = ^{
            [weakSelf doSomething];
        };

        #endif NSLog(@"myblock is %@", self.myblock);
    }
    return self;
}
  • MRC情况下,用__block可以消除循环引用。
  • ARC情况下,必须用弱引用才可以解决循环引用问题,iOS 5之后可以直接使用__weak,之前则只能使用__unsafe_unretained了,__unsafe_unretained缺点是指针释放后自己不会置

在上述使用 block中,虽说使用__weak,但是此处会有一个隐患,你不知道 self 什么时候会被释放,为了保证在block内不会被释放,我们添加__strong。更多的时候需要配合strongSelf使用,如下:

__weak __typeof(self) weakSelf = self; 
self.testBlock =  ^{
       __strong __typeof(weakSelf) strongSelf = weakSelf;
       [strongSelf test]; 
});

 

但是并不是所有的地方都要用到weakself,当self不持有,也不间接持有这个block的时候,就不需要使用weakself,比如
//使用coreanimation的时候不需要[UIView animateWithDuration:0.5 animations:^{
        NSLog(@"%@", self);
    }];
//使用Masonry的时候也不需要weakself
[self.headView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.equalTo(self.otherView.mas_centerY);
}];

 

 
 

 

 

 

 

 

 

 block的经典案例:

给UIView封装手势触发方法

UIView+AddClickedEvent.m- (void)addClickedBlock:(void(^)(id obj))clickedAction{
    self.clickedAction = clickedAction;
    // :先判断当前是否有交互事件,如果没有的话。。。所有gesture的交互事件都会被添加进gestureRecognizers中
    if (![self gestureRecognizers]) {
        self.userInteractionEnabled = YES;
        // :添加单击事件
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
        [self addGestureRecognizer:tap];
    }
}

- (void)tap{
    if (self.clickedAction) {
        self.clickedAction(self);
    }
}

 点击cell中的按钮触发vc中的block方法

//cell中的代码
//Block
@property (nonatomic, copy) void (^btnClickedBlock)(void);
// 点击方法
- (IBAction)btnClickedAction:(UIButton *)sender {
    if (self.btnClickedBlock) {
        self.btnClickedBlock();
    }
}

// vc中的代码
cell.btnClickedBlock = ^{
    //刷新当前cell
    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
};

 对AFNetWorking进行二次封装

+(void)quaryWithDic:(NSDictionary *)dic andUrlString:(NSString *)url :(void(^)(NSDictionary *))block{
    NSString * urlString = [NSString stringWithFormat:@"%@%@",SERVER,url];
    
    urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];;
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    [manager.requestSerializer setValue:@"1.0" forHTTPHeaderField:@"version"];
    [manager.requestSerializer setValue:@"ios" forHTTPHeaderField:@"terminal"];
    
    
    [manager.requestSerializer willChangeValueForKey:@"timeoutInterval"];
    manager.requestSerializer.timeoutInterval = 10.0f;
    [manager.requestSerializer didChangeValueForKey:@"timeoutInterval"];
    
    
    [manager POST:urlString parameters:dic progress:^(NSProgress * _Nonnull uploadProgress) {
        
        
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        
        
        NSLog(@"%@ net request is %@",urlString,extracted(responseObject));
        block(responseObject);
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        
        NSLog(@"the net request is failed ########################################## %@",error);
        block(@{@"code":@-1,@"message":@"网络连接错误"});
        
    }];

}

 block的链式写法

//1 在CaculateMaker.h文件中声明一个方法add:
CaculateMaker.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface CaculateMaker : NSObject

@property (nonatomic, assign) CGFloat result;

- (CaculateMaker *(^)(CGFloat num))add;

@end


//2 在CaculateMaker.m文件中实现add方法:
CaculateMaker.m
#import "CaculateMaker.h"

@implementation CaculateMaker

- (CaculateMaker *(^)(CGFloat num))add;{
    return ^CaculateMaker *(CGFloat num){
        _result += num;
        return self;
    };
}

@end

//3.3.3 在viewController里面导入CaculateMaker.h文件,然后调用add方法就完成了链式语法:
ViewController.m
CaculateMaker *maker = [[CaculateMaker alloc] init];
maker.add(20).add(30);

 

 

基本上是照着这个博客来学习的,这个博客写的非常好,very 牛逼

https://www.jianshu.com/p/bcd494ba0e22 

 

这个博客写的也很好,原理性方面写的比较多 

https://www.jianshu.com/p/14efa33b3562

 

 这个是面试向,可以参考

https://www.jianshu.com/p/4c652206a239

 

 

啊啊啊,好难过,已经六月14号了,颓废了半个月我卧槽

冰期时代已经玩腻了,都没啥好玩的了,如果不出新关卡就不想碰了
如果一年可以攒20w就好了,五年就能攒100w了,等社保到期就可以买一个小房子了嘿嘿嘿,房价应该比较稳定,慢慢涨的话还能接受

先找个18k的工作,然后有时间看看游戏怎么搞,我忽然理解了唐巧巧的感觉

 

我今天想起来,我不是因为王者荣耀不好玩才不玩的,我是因为手机不行才不玩的嘤嘤嘤

找到工作就换手机

垃圾小米

 

今天已经六月15号了,嗯嗯,日特码哦,今天一定要雄起!!!

 

好吧,依然没有雄起,今天是20号,今天一定要雄起!!!

 

好吧好吧,今天22号,今天不雄起了,今天雌起!

 

今天7月1号了哈哈哈,我现在很想死掉

今天7月4号了,我的心已经死了

 

 

今天玩游戏的时候,遇到了一个特别喜欢说话的队友,虽然感觉有点逗比,跟我的节奏也不太一样,但是输出很高,所以我瞬间就不介意他到处梦游了,加上另外一个打上单的一起,我们三个人打了好几局排位,每次都是碾压对方,感觉对面都是菜b的感觉,太爽了,终于有一种玩游戏的感觉了,哈哈哈,这种游戏果然还是要有好队友才玩的下去

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!