带有 XMLParser 的 AFNetworking HTTPClient 子类
Posted
技术标签:
【中文标题】带有 XMLParser 的 AFNetworking HTTPClient 子类【英文标题】:AFNetworking HTTPClient subclass with XMLParser 【发布时间】:2011-11-30 09:56:21 【问题描述】:我正在编写一个查询 XML REST Web 服务的小型 ios 应用程序。使用的网络框架是AFNetworking。
情况
要查询我将 AFHTTPClient 子类化的 web 服务:
@interface MyApiClient : AFHTTPClient
在实现中,我将其作为单例提供:
+ (MyApiClient *)sharedClient
static MySharedClient *_sharedClient = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^
_sharedClient = [[self alloc] initWithBaseUrl:[NSUrl URLWithString:@"http://url.to.the.webservice"]];
);
return self;
在 initWithBaseURL 中我告诉 AFNetworking 期待 XML 内容:
[self registerHTTPOperationClass:[AFXMLRequestOperation class]];
现在我可以从我的 ViewController 对单例调用 getPatch 并在成功块中开始解析我返回的 XML。然后,在 ViewController 中的 NSXMLParserDelegate 方法中,我可以选择我感兴趣的 XML 部分并对其进行处理。
问题
我希望在我的 HTTPClient 单例中拥有处理与 Web 服务相关的所有内容并返回数据模型或模型列表而不是 XML 的方法。
例如我想做这样的事情:
ServerModel *status = [[MyApiClient sharedClient] getServerStatus];
然后,ApiClient 将在内部调用 Web 服务,解析 XML 并返回模型。 我怎样才能做到这一点?通常我会使用一个在解析 XML 后被调用的委托,但是由于 ApiClient 的单例性质,可能会有多个委托?
希望有人能解释一下,谢谢!
【问题讨论】:
【参考方案1】:使用块代替委托。
来自我的 ApiClient 类:
- (void)getPath:(NSString *)path
parameters:(NSDictionary *)parameters
success:(void (^)(id response))success
failure:(void (^)(NSError *error))failure
NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];
[self enqueueHTTPOperationWithRequest:request success:success failure:failure];
-(void)fetchAllUsersSuccess:(void (^)(id))success
failure:(void (^)(NSError *))failure
[self getPath:@"/api/mobile/user/"
parameters:nil
success:^(id response)
if([response isKindOfClass:[NSXMLParser class]])
//parse here to new dict
success(newDict);
else
success(response);
failure:^(NSError *error)
failure(error);
];
现在我可以像这样使用它了:
ServiceApiClient *apiClient = [ServiceApiClient sharedClient];
[apiClient fetchAllUsersSuccess:^(id dict)
for (NSDictionary *object in [dict objectForKey:@"objects"])
[ServiceUser addUserFromDictionary:object
inContext:self.managedObjectContext];
NSError *error= nil;
[self.managedObjectContext save:&error];
if (error)
NSLog(@"%@", error);
failure:^(NSError * error)
NSLog(@"%@", error);
];
【讨论】:
我认为这在我的情况下不起作用,因为响应是必须首先解析的 XML(一个 NSXMLParser 对象)。解析 XML 发生在这个 NSXMLParser 对象的委托方法中,这意味着它在原始成功块之外...... 哦,之前已经解析过了。成功块适用于 NSDictionary。 在一个好的面向对象的设计中,ApiClient 现在应该只是如何与 api 对话,而模型对象的创建应该由模型类或工厂来完成。 您的代码在获取 JSON 时会运行良好,但使用 XML 我需要执行 [responseObject setDelegate:self]; [响应对象解析];在我的成功块中。然后在 NSXMLParserDelegate 方法中解析 XML(我认为这应该是 ApiClient 的任务?)并在 - (void)parserDidEndDocument:(NSXMLParser *)parser;模型已准备好返回给原始调用者。 解析应该由apiclient执行。但是对于像字典这样的通用交换对象。 ApiClient 本身可以是解析器委托或者它有一个成员。但你确定,这就是我所需要的吗? AFNetworking claims,它将立即使用 XML。【参考方案2】:(提前为这个“有点”的答案道歉,但我们正在努力寻找更好的解决方案......)
您需要退后一步,仔细考虑您的设计。
您遇到问题是因为您认为设计中的某些内容需要是单例,但是:
1)实际上没有必要,
2)可能已经存在可以为您完成这项工作的东西(例如您正在使用的 HTTP 库),
或
3) 你把错误的东西做成了单例,或者你没有把你的设计分成合适的部分来很好地配合单例的想法
那么,您能否明确告诉我您为什么要采用单例方法?是否只是为了确保一次只能发生一个网络请求?您的单例对象中是否有任何状态概念?然后我会更新这个答案或评论等。
(题外话:我还要补充一点,在某些情况下,可能确实需要一个“strong”单身人士——我的意思是确实存在只有一个可能的实例,并且该机制就像您正在做的那样直接嵌入到您的对象中-但这不是。替代方法是“weak”单例,我的意思是您的核心对象实际上,它像往常一样有一个普通的init
方法,但是对公共对象的共享访问是通过另一个对象进行的,这是一种简单的“工厂”,它实例化/保存共享实例。这个弱单例的优点想法是您的代码在不同的上下文中更具可重用性 - 例如,您可以决定稍后同时执行多个 HTTP 请求/会话 - 有时它可以减少编写测试的问题)。
【讨论】:
你可能是对的,我真的不需要单身人士。那为什么要用一个呢? 1. 在 AFNetworking 演示项目中是这样完成的:github.com/AFNetworking/AFNetworking/blob/master/iOS%20Example/… 2. 我喜欢单例访问网络类是多么容易...... [为什么我不能在 cmets 中插入换行符......] 作者“杀死”单例委托方法应该有效,所以你可能是对的 你上面的单例方法不应该返回_sharedClient
而不是self
吗?以上是关于带有 XMLParser 的 AFNetworking HTTPClient 子类的主要内容,如果未能解决你的问题,请参考以下文章
QT CMarkUp(XMLParser) 错误。 iconv.h:没有这样的文件或目录
UE4 C++解析与构建 XML 数据,XmlParser 与 tinyxml
忽略 xml.etree.ElementTree.XMLParser Python 中不匹配的标签
GroovyXml 反序列化 ( 使用 XmlParser 解析 Xml 文件 | 获取 Xml 文件中的节点和属性 | 获取 Xml 文件中的节点属性 )