【问题标题】:AFNetworking HTTPClient subclass with XMLParser带有 XMLParser 的 AFNetworking HTTPClient 子类
【发布时间】: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 的单例性质,可能会有多个委托?

希望有人能解释一下,谢谢!

【问题讨论】:

    标签: iphone objective-c cocoa-touch singleton afnetworking


    【解决方案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吗?
    猜你喜欢
    • 1970-01-01
    • 2011-09-30
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-10
    相关资源
    最近更新 更多