带有 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:没有这样的文件或目录

Swift4 中的 Codable 和 XMLParser

UE4 C++解析与构建 XML 数据,XmlParser 与 tinyxml

iOS XMLParser - 获取 url

忽略 xml.etree.ElementTree.XMLParser Python 中不匹配的标签

GroovyXml 反序列化 ( 使用 XmlParser 解析 Xml 文件 | 获取 Xml 文件中的节点和属性 | 获取 Xml 文件中的节点属性 )