如何在我的iOS应用程序中正确实现Services类?

我当前的头脑刮板:专门为我的rails应用程序的服务调用实现一个模型类。

这是场景:

  • 我有一个名为Service的类,它是NSObject的子类。
  • 实现文件定义了一些方法…让我们来看看doSignUp
  • 我正在使用AFNetworking与api进行通信。
  • 从我的SignUpViewController ,我创建了一个Service类的实例并调用doSignUp
  • 该方法按预期工作,我从服务器收到适当的响应。

现在来了我不完全明白的部分:

  • AFNetworking利用块进行服务调用。
  • 成功块内部,我调用了一个名为handleSignUp的辅助方法(也在Service类中)。 这个方法基本上解析了JSON,并从中创建了一个新的User (NSObject子类)。 然后, handSignUp方法返回User对象。

此时我有一个新的User对象,我需要将该对象发送回我的SignUpViewController …我该怎么做?

  • 我应该尝试将该对象添加到AppDelegate并从SignUpViewController访问它吗? 此解决方案可以用于访问各种全局属性,但SignUpViewController何时可以知道何时访问它?
  • 我应该尝试在Service类中添加对SignUpViewController的引用吗? 这似乎适得其反…我不妨将方法doSignUphandSignUp添加到SignUpViewController 。 在我看来,我的Service类不应该知道任何其他viewControllers。

请参阅下面的代码示例:

Service.h

//Service.h #import  #import "AFNetworking.h" @interface Services : NSObject - (void) doSignUp:(NSMutableDictionary*)params; @end 

Service.m

 // Service.m #import "Services.h" #import "Config.h" @implementation Services - (void) doSignUp:(NSMutableDictionary*)params { NSURL *url = [NSURL URLWithString:@"http://MYURL.COM"]; AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; NSURLRequest *request = [httpClient requestWithMethod:@"POST" path:@"signup.json" parameters:params]; AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { [self handleSignUp:JSON]; } failure:nil]; [operation start]; } - (User*) handleSignUp:(NSMutableArray*)json { User * user = nil; if ([[json valueForKey:@"success"] isEqualToString:@"true"]) { // create user here ... } return user; } 

SignUpViewController.h

 #import "Service.h" @interface SignUpViewController : UIViewController { Service * service; } @property (nonatomic, strong) Service * service; 

SignUpViewController.m

 #import "SignUpViewController.h" @interface SignUpViewController () @end @implementation SignUpViewController @synthesize service = __service; - (IBAction) initSignUp:(id)sender { // create params... [self.service doSignUp:params]; } 

再一次,所有这些代码都做了它应该做的事情……我只需要知道它应该如何进行通信。 如何警告SignUpViewController已调用handleSignUp并且新的User对象可用?

谢谢你的时间,安德烈

据我所知,你没有意识到这个过程的异步性质:实际上,由于网络或IO操作需要很长时间才能完成,我们倾向于选择异步访问,我的意思是:调用者(View Controller)调用消息通过服务的Web API,然后STOPS等待响应。 实际上,网络处理可以在不同的进程或线程或处理器的核心上完成。

那么,当操作完成时,如何通过服务通知调用者? 要做到这一点,我们必须使用Delegation绑定两个对象:我们创建一个协议(它只是一个对象将响应的消息声明),我们在View Controller中实现它,这样任何使用它的人都会知道确定有哪些消息可用。

然后我们将在我们的服务中声明一个类型为id的委托,一旦操作完成就会调用该委托。

这几乎就像在服务类中导入ViewController.h并为服务提供指向视图控制器的指针,但是以正确的方式完成(没有循环引用并遵守SOLID原则)

现在一些代码:

 //service.h @protocol RemoteApiProtocol  @required -(void) UserLoggingIn; -(void) UserLoggedIn:(User*)user; -(void) UserLoginError:(NSError *)error; @end 

该协议就像一个小接口,它是两个类/对象之间的契约。 然后在您的服务中,您可以声明协议的字段和属性:

 //Service.h #import  #import "AFNetworking.h" @interface Services : NSObject{ __weak id delegate; } @property (nonatomic, assign) id delegate; - (void) doSignUp:(NSMutableDictionary*)params; @end 

此时,您在视图控制器中实现协议,集成您刚刚构建的合同。 这样,服务器将知道委托将始终能够响应协议消息。

 //SignUpViewController.h #import "Service.h" @interface SignUpViewController : UIViewController  { Service * service; } @property (nonatomic, strong) Service * service; 

在实现协议之后,您可以将视图控制器指定为服务器的委托,这样服务器在调用apis之后将能够使用调用结果调用委托。 为此,您需要实现服务将调用的协议消息:

 //SignUpViewController.m #import "SignUpViewController.h" @implementation SignUpViewController @synthesize service = __service; - (IBAction) initSignUp:(id)sender { //create the service with params //... //assign self as the delegate self.service.delegate = self; [self.service doSignUp:params]; } #pragma mark - RemoteApiProtocol -(void) UserLoggingIn { //login started, //you can show a spinner or animation here } -(void) UserLoggedIn:(User*)user { //user logged in , do your stuff here } -(void) UserLoginError:(NSError *)error { NSLog(@"error: %@",error); } @end 

最后,在成功和错误块中调用api之后,调用服务中的消息:

 // Service.m #import "Services.h" #import "Config.h" @implementation Services - (void) doSignUp:(NSMutableDictionary*)params { NSURL *url = [NSURL URLWithString:@"http://MYURL.COM"]; AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; NSURLRequest *request = [httpClient requestWithMethod:@"POST" path:@"signup.json" parameters:params]; AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { [self handleSignUp:JSON]; } failure:nil]; //Signaling the start of the signup operation to the delegate if(self.delegate){ [self.delegate UserLoggingIn]; } [operation start]; } - (User*) handleSignUp:(NSMutableArray*)json { User * user = nil; if ([[json valueForKey:@"success"] isEqualToString:@"true"]) { // create user here ... //call the delegate (view controller) passing the user if(self.delegate){ [self.delegate UserLoggedIn:user]; } }else{ //error handling if(self.delegate){ //NSError *error = ... //[self.delegate UserLoginError:error]; } } //return user; } @end 

您可能希望查看Delegation和/或Data Source 。 在本文中查看我接受的答案以获取示例,并参考有关该主题的Apple Docs。

目标C查看控制器通信

希望这可以帮助。

将回调块参数添加到doSignUp (应该可以重命名为signUpWithParams:success:failure: ,以便控制器可以响应注册成功或失败,并使任一情况的上下文可用于向用户显示相关信息。

委派方法肯定会奏效。 您基本上可以将一个委托添加到Service类,并在调用doSignup之前将委托设置为等于视图控制器。

但是,我会使用块来实现它。 将参数添加到doSignUp方法,该方法是一个完成块,在SignUpViewController中声明。 有关如何将块作为参数传递的示例,请查看AFNetworking中的源。