4

私の現在のヘッド スクラッチャー: Rails アプリケーションへのサービス呼び出し専用のモデル クラスを実装しています。

シナリオは次のとおりです。

  • NSObject のサブクラスであるServiceという名前のクラスがあります。
  • 実装ファイルにはいくつかのメソッドが定義されています… doSignUpを見てみましょう。
  • AFNetworkingを使用して API と通信しています。
  • SignUpViewControllerからServiceクラスのインスタンスを作成し、 doSignUpを呼び出します。
  • メソッドは期待どおりに機能し、サーバーから適切な応答を受け取ります。

今、私が完全に理解していない部分が来ます:

  • AFNetworkingは、サービス呼び出しにブロックを利用します。
  • 成功ブロック内で、handleSignUpというヘルパー メソッドを呼び出します(これもServiceクラスにあります)。このメソッドは本質的に JSON を解析し、そこから新しいUser (NSObject サブクラス) を作成します。次に、handSignUpメソッドはUserオブジェクトを返します。

この時点で、新しいUserオブジェクトができたので、そのオブジェクトをSignUpViewControllerに送り返す必要があります。どうすればよいですか?

  • そのオブジェクトを AppDelegate に追加して、SignUpViewControllerからアクセスする必要がありますか? このソリューションは、さまざまなグローバル プロパティにアクセスするために機能する可能性がありますが、SignUpViewController はいつ アクセスすべきかを知るのでしょうか?
  • ServiceクラスにSignUpViewControllerへの参照を追加する必要がありますか? それは逆効果のようです... doSignUp メソッドとhandSignUpメソッドを SignUpViewControllerに追加することもできます。私のServiceクラスは他のviewControllerを認識してはいけないようです。

私のコード例については、以下を参照してください。

Service.h

//Service.h
#import <UIKit/UIKit.h>
#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];
 }

繰り返しますが、このコードはすべて本来の目的を果たします...すべての通信方法を知る必要があるだけです。handleSignUpが呼び出され、新しいUserオブジェクトが使用可能になったことをSignUpViewControllerに通知するにはどうすればよいですか?

お時間をありがとう、アンドレ

4

4 に答える 4

4

私が見る限り、あなたはこのプロセスの非同期性を認識していません。実際には、ネットワークまたは IO 操作が完了するまでに長い時間がかかるため、非同期アクセスを好む傾向があります。つまり、呼び出し元 (View Controller) がサービスを介して web API を呼び出し、応答待ちを停止します。実際には、ネットワーク処理は別のプロセス、スレッド、またはプロセッサのコアで実行できます。

では、操作が完了したときにサービスから呼び出し元に通知するにはどうすればよいでしょうか。これを行うには、デリゲーションを使用して 2 つのオブジェクトをバインドする必要があります。プロトコルを作成し (オブジェクトが応答するメッセージの宣言にすぎません)、それをビュー コントローラーに実装します。どのメッセージが利用可能かを確認してください。

次に、操作が完了すると呼び出されるタイプ id のデリゲートをサービスで宣言します。

これは、ViewController.h をサービス クラスにインポートし、サービスにビュー コントローラーへのポインターを与えるようなものですが、正しい方法で行われます (循環参照なしで、SOLID 原則を尊重します)。

今いくつかのコード:

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

プロトコルは小さなインターフェイスのように機能し、2 つのクラス/オブジェクト間の契約です。次に、サービスで、プロトコルのフィールドとプロパティを宣言できます。

//Service.h
#import <UIKit/UIKit.h>
#import "AFNetworking.h"
@interface Services : NSObject{
      __weak id<RemoteApiProtocol> delegate;
}

@property (nonatomic, assign) id<RemoteApiProtocol> delegate;

- (void) doSignUp:(NSMutableDictionary*)params;

@end

この時点で、View Controller にプロトコルを実装し、作成したコントラクトを統合します。このようにして、サーバーはデリゲートが常にプロトコル メッセージに応答できることを認識します。

//SignUpViewController.h
#import "Service.h"
 @interface SignUpViewController : UIViewController <RemoteApiProtocol> {
     Service * service;
 }

 @property (nonatomic, strong) Service * service;

プロトコルを実装した後、View Controller をサーバーのデリゲートとして割り当てることができます。このようにして、サーバーは API を呼び出した後、呼び出しの結果でデリゲートを呼び出すことができます。これを実現するには、サービスによって呼び出されるプロトコル メッセージを実装します。

//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
于 2012-06-13T17:14:46.403 に答える
1

コールバック ブロック パラメーターを に追加しますdoSignUp(おそらく名前を に変更する必要がありますsignUpWithParams:success:failure:)。これにより、コントローラーはサインアップの成功または失敗に応答し、ユーザーに関連情報を提示するために使用可能ないずれの場合のコンテキストも持つことができます。

于 2012-06-13T15:19:08.297 に答える
1

Delegationおよび またはを調べることができますData Source。例については、この投稿で受け入れられた回答と、件名に関する Apple Docs への参照を確認してください。

Objective C ビューからコントローラーへの通信

お役に立てれば。

于 2012-06-13T03:45:24.420 に答える
0

委任方法は間違いなく機能します。基本的に、サービスクラスにデリゲートを追加し、doSignupを呼び出す前にデリゲートをViewControllerと等しく設定することができます。

ただし、ブロックを使用して実装します。SignUpViewControllerで宣言されている完了ブロックであるdoSignUpメソッドにパラメーターを追加します。ブロックをパラメーターとして渡す方法の例については、AFNetworkingからソースを確認してください。

于 2012-06-13T06:50:17.793 に答える