iOS block的再会

作者: Auditore | 来源:发表于2018-01-16 21:22 被阅读24次

    之前写过一篇iOS block的一些自我见解, 期间修改过好几次, 但总觉得理解的还是不够深刻, 现在翻出来又整理了一次, 再谈谈block作为函数参数的使用.

    首先我肯定大家都是见过block的, 用过block的, 系统提供的很多方法, 都会用block作为回调. block是一种匿名函数, 那么为什么叫匿名函数?

    就如名字暗示的一样,匿名函数实际上就是一个没有名字或者标示(identifier)的函数。匿名函数只有内容(也可以叫做body),我们可以将其存储在一个变量中,以便之后使用,或者将其当做一个参数传递给另外一个函数使用。

    那怎么用block呢?

    1.声明与定义block

    int (^sawBlock)(int a, int b) = ^(int numb1, int numb2){
            return numb1 + numb2;
    };
    

    上面就是声明了一个block并且定义了它.我们调用的时候, 就和普通函数一样调用就行.
    int a = sawBlock(1, 2)
    我们声明block还有一种方式, 那就是tpyedef
    typedef int (^sawBlock)(int a, int b);
    这样, 我们就可以直接用sawBlock来指代上面那个block了, 但要注意, 现在的sawBlock是一个类型了, 而不是指具体的某个对象, 所以声明就成了下面这样

    sawBlock saw = ^(int a, int b){
       return a + b;
    };
    saw(1, 2);
    

    easy!

    2.作为函数参数使用

    我们经常看到把block作为参数放入函数中,

    - (void)tiantian:(void(^)(NSString *yourname))yourName{
        yourName(@"hahah");
    }
    //block暂时保存了你传入的参数, 使用这个方法, 可以获取到它保存的参数.
    [self tiantian:^(NSString *name) {
            NSLog(@"%@",name);
    }];
    

    作为函数参数的应用

    网络请求, 然后返回请求结果, 如果我们定义一个方法返回值NSdictionary *result用来接收请求返回的结果. 又因为现在我们的请求都是用NSURLSession发起的, 而NSURLSession都是用block来获取请求结果的, 如果我们对请求进行了封装, 那我们不是得等NSURLSession的block回调结果?我们怎么才能第一时间获取到block里的值呢?
    一种很low的方法, 让block变成同步, 使用信号量.

    - (NSDictionary*)requestResultByIPStr:(NSString*)ipStr andDominNameString:(NSString*)dominName{
        if (!ipStr.length||!dominName.length) {
            NSLog(@"request ipStr or dominName missed");
            [self addLogToArr:@"请求中IP或者域名缺失"];
            return nil;
        }
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/?domain=%@&type=json&IP=",ipStr,dominName]];
        __block NSDictionary *resultDic = nil;
         NSURLSession *session = [NSURLSession sharedSession];
        __block   NSURLRequest *request = [NSURLRequest requestWithURL:url];
        NSMutableURLRequest *muteRequest = [request mutableCopy];
        muteRequest.timeoutInterval = 5;
        __block   dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        NSURLSessionTask *task = [session dataTaskWithRequest:muteRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (data) {
                NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
                if (dic.count) {
                    resultDic = dic;
                }else{
                    resultDic = nil;
                }      
            }
            dispatch_semaphore_signal(sema);
        }];
        [task resume];
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        return resultDic;
    }
    

    是不是感觉浑身不舒服, 这一大堆的代码看着就很僵硬. 别急, 我们用block改进下, 见证奇迹的时刻到了!利用block可以保存参数的特性, 我们这样写:

    - (void)requestResultByIPStr:(NSString*)ipStr andDominNameString:(NSString*)dominName withCompletion:(void(^)(NSDictionary *resultDic,NSError *error, NSURLResponse *response))compeletion{
        if (!ipStr.length||!dominName.length) {
            NSLog(@"request ipStr or dominName missed");
            [self addLogToArr:@"请求中IP或者域名缺失"];
            return;
        }
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/?domain=%@&type=json&IP=",ipStr,dominName]];
        NSURLSession *session = [NSURLSession sharedSession];
        __block   NSURLRequest *request = [NSURLRequest requestWithURL:url];
        NSMutableURLRequest *muteRequest = [request mutableCopy];
        muteRequest.timeoutInterval = 5;
        NSURLSessionTask *task = [session dataTaskWithRequest:muteRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (data) {
                NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
                compeletion(dic,error,response);
            }
     
        }];
        [task resume];
    }
    

    使用起来也方便

    [self requestResultByIPStr:@"ip" andDominNameString:@"port" withCompletion:^(NSDictionary *resultDic, NSError *error, NSURLResponse *response) {
            NSLog(@"%@",resultDic);
    }];
    

    现在只有19行代码了, 而之前是27行! 将block作为方法的参数定义在方法上, 接着在我们需要的地方将我们要返回的变量传入block中, 这样在我们调用这个方法的时候, 通过使用block自带的形参就能获取到之前存于block中的变量了!而且block是异步的, 我们想在获取到数据以后执行代码, 就在block内部实现就好了.

    当然block它也可以保存代码片段, 看你怎么用了.

    那么, block是如何神奇地完成华丽转身的呢?

    参考下:OC中, block(块)的本质是什么? - Joshua Shen的回答 - 知乎
    一个block本质上就是一个函数指针,即那个代码块的内存地址。block常用作传值,实际上就是把block的地址传到要调用block的地方。

    3.结尾

    就写到这, 关于别的应用场景, 等下次想到再写.

    相关文章

      网友评论

        本文标题:iOS block的再会

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