之前写过一篇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.结尾
就写到这, 关于别的应用场景, 等下次想到再写.
网友评论