1

我正在编写一个查询 XML REST Web 服务的小型 iOS 应用程序。使用的网络框架是AFNetworking

情况

要查询 Web 服务,我将 AFHTTPClient 子类化:

@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 的单例性质,可能有多个委托?

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

4

2 回答 2

4

使用块而不是委托。

从我的 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);
}];
于 2011-11-30T10:59:43.163 回答
0

(提前为这个“有点”的答案道歉,但我们正在努力寻找更好的解决方案......)

您需要退后一步,仔细考虑您的设计。

您遇到问题是因为您有一个想法,即您的设计中的某些内容需要是单例,但是:

1)这实际上不是必需的,

2)可能已经存在为您完成这项工作的东西(例如,您正在使用的 HTTP 库),

或者

3)你把错误的东西做成了单例,或者你没有把你的设计分成适当的部分来很好地配合单例的想法

那么,您能明确告诉我为什么要采用单例方法吗?是否只是为了确保一次只能发生一个网络请求?您的单例对象中是否有任何状态概念?然后我会更新这个答案或评论等。

题外话:我还要补充一点,在某些情况下,可能确实需要一个“”单例——我的意思是实际上只有一个可能的实例,并且该机制直接嵌入到您的对象中,就像您正在做 - 但不是这样。替代方案是“”单例,我的意思是你实际完成工作的核心对象有一个简单的init方法和往常一样,但是对公共对象的共享访问是通过另一个对象进行的,这是一种简单的“工厂”,它实例化/保存共享实例。这种弱单例想法的优点是您的代码在不同的上下文中更可重用 - 例如,您可以决定稍后同时执行多个 HTTP 请求/会话 - 有时它可以减少编写测试的问题)。

于 2011-11-30T11:01:59.660 回答