ios afnetworking 传值为什么接收到null

 我来答
許你一生无怨
2016-07-20 · 知道合伙人软件行家
許你一生无怨
知道合伙人软件行家
采纳数:2535 获赞数:4697
北京同方光盘股份有限公司2015年度优秀员工

向TA提问 私信TA
展开全部
其实每一个API基本上都是URI不同,假如我们的API地址是http://api.chengxufan.com/一个创建文章的具体地址就可能是http://api.chengxufan.com/article/create,起初我在一个类里面定义了若干个函数,每个函数都使用了AFNetworking来进行网络通讯,可想而知这个类的代码会很臃肿,后来我把每个API都继承了这个类,这样每一个API都是一个单独的类,看上去不错,这样不同的程序员之间就不会冲突了,而且每个文件也真不大,但每个文件不大但是还要写。

这又带来了新的问题,IOS开发不是java引入文件是很头大的事情,每个类都要先引用在使用,这是多降低工作效率的事啊,于是我就想干掉这些类,最后我终于想明白了,只定义URI放到一个叫做define.h的文件里,在写一个通用的LJsonRequest类,这样我只需要引入两个文件所有的地方就可以用了,一个是define.php另一个是LJsonRequest.h即可,放到你项目的Supporting Files/ew-Prefix.pch文件里就可以了,这样所有的地方都不用特别的去引用了。

其中, define.php中的内容大概是这样子:

#define API_ARTICLE_LIST @"article/list"
#define API_ARTICLE_VIEW @"article/view"
调用的时候大概是这样子:

[LJsonRequest execute: API_ARTICLE_LIST params:nil callback:^(LResponse *response) {
...
}];

[LJsonRequest execute: API_ARTICLE_VIEW params:nil callback:^(LResponse *response) {
...
}];
问题2 简化IOS的[[Xxx alloc]init]
起初我采用:

LJsonRequest request = [[LJsonRequest alloc]init];
[request execute: API_ARTICLE_VIEW params:nil callback:^(LResponse *response) {
...
}];
每次这样写浪费一些创建的开销在IOS程序里不算什么,有时候我只想让写代码效率高一些,于是我封装了一个post方法里面把这些事情都做了,而且还是单例模式的,于是我用下面的代码实现了直接调用execute方法的实现。

#import "LJsonRequest.h"
@implementation LJsonRequest
static LJsonRequest *instance = nil;
static dispatch_once_t onceToken;

+(LJsonRequest*) sharedJsonRequest {
dispatch_once(&onceToken, ^{
if(instance = nil) {
instance = [[super allocWithZone:NULL]init];
}
}
return instance;
}

+(BOOL) execute:(NSString*) uri params:(NSDictionary *)params callback:(REQUEST_CALLBACK)callback {
LJsonRequest* jr = [self sharedJsonRequest];
return [jr _execute:uir params:params callback:callback];
}

+(BOOL) _execute:(NSString*) uri params:(NSDictionary *)params callback:(REQUEST_CALLBACK)callback {
...
}
其中,REQUEST_CALLBACK是在define.h里定义的宏:

#define REQUEST_CALLBACK void (^)(LResponse *response)
因为很多地方都需要这样的定义,每次写起来很费劲,宏是C时代的产物,该用的地方还是可以用的。

问题3 封装LResponse
AFNetworking都是异步的调用,所有内容都需要通过回调的方式来继续执行,回调的参数过少不够用,例如有时候你想看看返回来的是些什么错误,有时候你想看看接口里返回的原始信息是什么,有时候你只想要里面的数据,我把这些都封装到了LResponse里。

#import <Foundation/Foundation.h>

@interface LResponse : NSObject
@property(strong, nonatomic) NSDictionary *header;
@property(strong, nonatomic) NSNumber *status;
@property(strong, nonatomic) NSString *messsage;
@property(strong, nonatomic) NSDictionary *body;
@property(strong, nonatomic) NSString *text;
@property(strong, nonatomic) NSError *error;
@end
因为很多地方都会用到这个类,所以我们一样把这个类的头文件#import到Supporting Files/ew-Prefix.pch下,这样基本所有的地方也不用再次引用这个文件头。

可以看到出来我把返回的结果给细分到了多个成员变量里,整个内容输出用的json结构,大概是这样:

{
status: 0,
header: {
...
},
body: [
{'title' => 'title1'},
{'title' => 'title2'}
],
'message' => 'ok'
}
status 存放状态信息
header 存放头信息
body 存放正文内容
message 一般只有status是负数的时候才去看看返回的错误信息
error 是AFNetworking返回给我们的错误信息
text 是页面返回来的原始信息
有时候页面里有其它的错误,导致JSON输出的不完整,或者输出的是其它信息,我们就需要看看原始的内容。
这样我们的一个具体controller就可以这样来利用LResponse的返回结果,一般情况下,你只需要关心body里的内容:

NSDictionary *params = [[NSDictionary alloc] initWithObjectsAndKeys:self.articleId, @"id", nil];
[LJsonRequest execute: API_ARTICLE_VIEW params:params callback:^(LResponse *response) {
self.tibleLabel.text = [response.body objectForKey:@"title"];
self.bodyTextView.text = [response.body objectForKey:@"body"];
}];
问题4 检查网络状态和API异常
这两个问题一定要分清楚,网络状态不佳的时候应该提示用户“网络不给力”,如果是API异常应该提示出“服务器错误,请您稍后刷新试试。”。

你无须把这些代码写到不同的controller里,你只需要改进LJsonRequest类即可。

我首先想到的是在_execute()函数的内部增加了两个调用,一个调用是beforeExecute()一个调用是afterExecute(),这两个函数帮助我在执行网络操作的前后判断出网络的状态,其中beforeExecute()检查网络,另外一个弹出不同的提示,大概代码是这样:

- (void)_execute:(NSString*) uri params:(NSDictionary *)params callback:(REQUEST_CALLBACK)callback{
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:API_BASE_URL]];

if (![self beforeExecute:manager]) {
return;
};

[manager POST:uri parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
LResponse *response = [[LResponse alloc]init];
response.text = operation.responseString;
response.header = [responseObject objectForKey:@"header"];
response.status = [responseObject objectForKey:@"status"];
response.body = [responseObject objectForKey:@"body"];
Log(@"%@ response.body=%@", uri, response.body);
response.messsage = [responseObject objectForKey:@"message"];
response.error = nil;
if(callback != nil)
callback(response);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
LResponse *response = [[LResponse alloc]init];
response.text = operation.responseString;
response.header = nil;
response.body = nil;
response.messsage = nil;
response.error = error;
response.status = [[NSNumber alloc]initWithInt:-1];
[self afterExecute:response];
callback(response);
}];
}

- (BOOL)beforeExecute:(AFHTTPRequestOperationManager*)manager {
NSOperationQueue *operationQueue = manager.operationQueue;

[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
[operationQueue setSuspended:NO];
break;
case AFNetworkReachabilityStatusNotReachable:
default:
[operationQueue setSuspended:YES];
break;
}
}];
[manager.reachabilityManager startMonitoring];
return true;
}

- (BOOL) afterExecute:(LResponse *) response {

if (response.error == nil) {
return TRUE;
}

if ([response.error code] == NSURLErrorNotConnectedToInternet) {
[self alertErrorMessage:@"网络不给力"];
return FALSE;
}

Log(@"server error text is %@", response.text);
[self alertErrorMessage:@"服务器故障,请稍后再试。"];

return true;
}
这里你需要了解一下AFNetworking判断网络状态的方法,这是最标准的方法,检查网络状态和你顺序执行的请求没有在一个线程里,于是用了NSOperationQueue来控制,当网络状态为不可用的时候,会发出通知,这时候你所执行的请求会被立刻中断执行,回到failure里,回来的错误如果是NSURLErrorNotConnectedToInternet就是网络不好,否则就是服务器故障,具体细节用户是不用了解那么详细的。

alertErrorMessage是我另外实现的一个方法,用来弹出信息。

这样的设计完全可以在用户触发请求的时候才去做实际的判断,在这一层封装不会遗漏任何地方。

问题5 解决调试错误输出的问题
起初我们调试的时候,如果API本身有问题,看不到输出,于是我在afterEexecute()函数里增加了一行代码,这样在出现错误的时候就很快能看出来对方的原始内容是什么,这很可能是PHP的某一行的报错信息,代码看上面的情况,Log是我自己的一层宏定义,包装了CLog。

问题6 session问题
因为很多的程序是通过session机制和远程API进行数据交换的,所以我在LJsonRequest的上面又封装了一个LSessionRequest类,它的作用是当session不存在的时候就去远程去协商一个新的session_id过来。

问题6 满足不同场景的需要
大多数的页面仅仅存在两种数据调用的方式,一种是加载内容到列表展示,一种是发送请求到远端去执行,折中行为其实只和页面有关,也就是Controller这一层有关,因此我又定义了一个LURequest放在了LSessionRequest的下层,里面实现了两个更容易看懂的方法:

#import <Foundation/Foundation.h>

@interface LURequest : NSObject
+(void)post:(NSString*) uri params:(NSDictionary*)params callback:(REQUEST_CALLBACK)callback;

+(void)load:(NSString*) uri params:(NSDictionary*)params view:(UIView*)view callback:(REQUEST_CALLBACK)callbac;
@end
其中post()方法用来提交表单和投递数据,load()方法用来加载数据,两个场景不一样,post需要弹出一个提示,例如告诉用户,正在发送请求。另外一个现在常用的做法是当load数据的时候给当前窗口放一个背景图,当加载完成的时候隐藏掉。

当然,如果你的APP还有更多的场景你应该多实现几个。

小结 类之间的关系
LJsonRequest AFnetworking的封装
LResponse 返回信息的封装
LSessonRequest 带有session信息的LJsonRequest实现。
LURequest 带有使用场景的LSessionRequest类的实现。
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式