这周在开始写东西的时候遇到了很多的坑,首先就是网络请求异步的问题.
整理了一下思路.传值主要使用了以下的两种思路.
我们在进行网络请求时.在使用 Alamofire 和 AFNetWorking 的时候都是在进行异步请求.
举个栗子 :
闭包传值
#define MapAnalyzeBaseURL "http://restapi.amap.com/v3/geocode/regeo?"
#define MapAnalyKey "&key=e8ba6e7f8fd9699f47affcd525f1f300"
#import "AFNetHelper.h"
@implementation AFNetHelper
-(void)setNetWorkHelpercompletionHandler: ( void (^) (id))Myblock{
NSURLSession *configuration = [NSURLSessionConfiguration defaultSessionConfiguration ];
AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:configuration];
NSString *URLString = @MapAnalyzeBaseURL;
NSDictionary *paramters = @{@"output":@"JSON",@"location":@"116.310003,39.991957",@"key":@"e8ba6e7f8fd9699f47affcd525f1f300",@"radius":@"1000",@"extensions":@"all" };
NSMutableURLRequest * request = [[AFHTTPRequestSerializer serializer]requestWithMethod:@"GET" URLString:URLString parameters:paramters error:nil ];
NSURLSessionDataTask * task = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if(error){
NSLog(@"error");
}else{
NSLog(@"%@",responseObject);
Myblock(responseObject);
}];
[task resume];
}
@end
这里有一段网络请求,我们需要请求后返回的数据.但是死活就是拿不到数据.可能你打断点到responeObject 会发现已经有了值,但是你在这个里面想把值传出去.你会发现很难传出去,
一些传值时候的错误想法
假设你现在进行了网络请求 ,拿到了一个JSON数据这个数据解析出来是一个数组.接着你想要把这个数组传出去.可能要去更新你的界面.接着就自然而然的定义了一个类属性,然后想当然的就会写赋值语句.既然我需要这个值,那么直接赋值给一个类属性.然后再用这个属性来对界面进行更新.一切都是很自然的事情.当你把一切都做好了,开始运行的时候,会发现程序除了问题.再用类属性对界面赋值的时候.你会发现控制台显示你的这个属性是空? WHY? 这为什么是空的
举个栗子
接着执行这个命令,你会发现很有意思的事情
这很正常╮(╯﹏╰)╭ 多简单就是这么随意 ,接着开始执行下一行.
( ⊙o⊙?)不懂 这是怎么回事,为什么前面的这些断点都没执行,直接就结束了?怎么回事,为什么不执行,顺序执行,多简单的事啊,为什么不执行.思前想后,无解.
接下来很小心的检查了一下成功后的数据,发现,我数据已经拿了回来.而且也已经赋值给了全局属性.那为什么就是没有值呢?
这还要从刚才那个程序不顺序执行,直接返回结果开始说.这个网络请求是异步的.所以它会在其他的线程去执行.而不是主线程,所以它直接把网络请求扔给了其他线程去做,然后程序跳过这段继续进行.因为这个时候网络请求并没有被执行,所以我们的类属性中的值是空的,这个时候去用这个类属性值去更新界面上的一些东西.会出错.程序出BUG了
既然已经有了问题,我们应该怎么去解决?
解决方法一:
既然网络请求没有执行,那么我们就等网络请求执行完,我们的类属性已经拿到了数据之后再更新界面.
在swift 中有一种属性叫 Property Observers(监视器属性)开始的时候设置一个默认值,然后当值改变的时候,去更新界面
举个栗子
var weekWeather = [Daily](){
didSet{
self.setNowWeather()
}
}
Alamofire.request(.GET, url).validate().responseJSON { respon in
if let dic = respon.result.value as? NSDictionary {
if let newDic = dic["HeWeather data service 3.0"]?.firstObject as? NSDictionary{
if let weatherArray = newDic["daily_forecast"] as? NSArray{
let daily = Mapper<Daily>().mapArray(weatherArray)
print(daily?.count)
self.weekWeather += daily!
}
}
}
}
解决方法二
我们对整个网络请求的部分进行封装.添加闭包,用闭包来解决传值的问题 .就是我们最开始写的那个闭包传值.
这个部分有两个地方值得大家注意
首先我们声明了这个函数接受一个闭包类型.这个闭包接受id类型的参数,返回值为空
接下来我们要执行这个闭包
当我们成功的拉取到了网络的数据的时候,执行这个闭包. (ˉ▽ ̄~) 切~~,声明了执行了又怎么样,你的执行代码呢,你连一行执行的代码都没写,还执行闭包.能执行才出鬼了.
接下来就是闭包的执行部分
这里因为我的工程里面是oc 和 swift 混编,但是基本的思想是一样的,我创建了一个类的实例(刚开始写的那个类).然后执行了我声明的方法,最最关键的地方到了.我们这里接受一个闭包的参数!!!.还记得我们执行闭包的时候的那个参数么,这个时候闭包就把这个参数拿到了.而且参数是有值得.因为不需要这个闭包有返回值.只是要拿到网络请求的结果responseObject.这是一个id类型的变量.在这个闭包里面.data 指的就是这个 responseObject. 可能有人问这是怎么看出来的
Myblock(responseObject);
还记得我们这行代码么,这行代码就是闭包接受的参数.我们已经给了一个参数叫
responseObject.然后在闭包执行的时候
a.setNetWorkHelpercompletionHandler{ data in
这个data 就是我们抓取到的responseObject.这样,我们就拿到数据了.接下来就可以在这个闭包中更新你的类属性,然后更新你的界面了.
总结
网络请求异步的问题在开发中很常见,开始不能赋值的问题纠结了好几天,最终发现这个api是异步的,并不会直接执行.所以我们在使用一些异步的API的时候需要注意传值的问题.