2

コードに、追跡できない非常に奇妙でランダムな問題が見られます。AFNetworking JSON リクエスト メソッドから戻ると、データ モデルの init メソッドでクラッシュが発生します。アプリがクラッシュした場合、コール スタックに戻り、JSON 要求/応答が何であったかをデバッグできます。奇妙な部分は、URL、リクエスト、および resonseJSON をチェックするときです。responseJSON が URL/リクエストの期待される結果と一致しません。他の API メソッドの呼び出しとデータを取得しているようです。データ/JSON は、モデルの初期化時にアプリがクラッシュすると予想されるものではないためです。

通常、返されるデータは異なり、常に同じとは限りません。データがエンドポイント A からのものである場合もあれば、B からのものである場合もあり、一貫性がありません。ただし、同じモデル オブジェクトで一貫してクラッシュするようです。

エンドポイント A のデータを要求しますが、エンドポイント B のデータが返されます。クラッシュしたときに AFHttpOperation をデバッグすると、これが結果であることがわかります。2 つの呼び出しが交差しているようで、ある種の競合状態です。以下は、私のモデル オブジェクト、Rest クライアント、およびモデル アクセス レイヤーのサンプルです。

モデル オブジェクト

@implementation Applications

- (id)initWithData:(NSDictionary *)appData forLocation:(Location *)location inCategory:(Category *)category {
    // appData is the JSON returned by The Rest Client and AFNetworking
    self = [super init];
    DDLogVerbose(@"appData = %@", appData);
    if (self) {
        _location = location;
        _listeners = [NSMutableArray mutableArrayUsingWeakReferences];
        _devices = [[NSMutableDictionary alloc] init];
        _category = category;
        _subscriptions = [Utility sanitizeArray:appData[@"Subscriptions"]];
    }
    return self;
}

@end

@implementation Location

- (void)refreshApplications {
    [[Model shared] appsForLocation:self 
                            success:^(NSObject *obj) {
        self.apps = nil; //we have to get our apps again
        self.apps = [NSMutableArray array];

        NSArray *newApps = (NSArray *) obj;
        for (NSDictionary *app in newApps) {
            **// This is where it's crashing!**
            Applications *newApp = [[Applications alloc] initWithData:app
                                                          forLocation:self
                                                           inCategory:[[SmartAppCategory alloc] init]];
            [self.apps addObject:newApp];
        }

        [self notifyListeners];
    }
                            error:nil];
}

@end

残りのクライアント

@interface Rest

+ (Rest *)sharedClient;
- (void)GET:(NSString *)path parameters:(NSDictionary *)params success:(SuccessCallback)sCallback error:(ErrorCallback)eCallback;

@end

@implementation Rest

+ (Rest *)sharedClient {
    static dispatch_once_t token;
    static Rest *shared = nil;
    dispatch_once(&token, ^{
        shared = [[Rest alloc] init];
    });
    return shared;
}

- (id)init {
    self = [super init];
    if (self) {
        [self createClients];
    }
    return self;
}

- (void)createClients {
    // Setup the Secure Client
    // Private implementation properties
    self.secureClient = [[AFOAuth2Client alloc] initWithBaseURL:baseUrl clientID:OAUTH2_CLIENT_ID secret:OAUTH2_CLIENT_SECRET];
    [self.secureClient setParameterEncoding:AFJSONParameterEncoding];
    AFOAuthCredential *credential = (AFOAuthCredential *) [NSKeyedUnarchiver unarchiveObjectWithData:[KeyChainStore dataForKey:KEYCHAIN_SETTINGS_AFOAuthCredential]];
    if (credential) {
        [self.secureClient setAuthorizationHeaderWithToken:credential.accessToken];
    }

    // Setup the Anonymous Client
    self.anonymousClient = [[AFHTTPClient alloc] initWithBaseURL:baseUrl];
    [self.anonymousClient setParameterEncoding:AFJSONParameterEncoding];
    [self.anonymousClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
}

- (void)GET:(NSString *)path parameters:(NSDictionary *)params success:(SuccessCallback)sCallback error:(ErrorCallback)eCallback {

    [_secureClient getPath:path
                parameters:params
                   success:^(AFHTTPRequestOperation *operation, id responseObject) {
                       DDLogVerbose(@"Success Path: %@ JSON: %@", path, responseObject);
                       if (sCallback) sCallback(responseObject);
                   }
                   failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                       [Rest callErrorBlock:eCallback withOperation:operation];
                   }];
}

@end

モデル アクセス層

@interface Model

+ (Model *)shared;
- (void)appsForLocation:(Location *)location success:(SuccessCallback)success error:(ErrorCallback)error;

@end

@implementation Model

- (void)appsForLocation:(Location *)location success:(SuccessCallback)success error:(ErrorCallback)error {
    NSString *path = [NSString stringWithFormat:@"/api/locations/%@/apps/", location.locationId];

    [[Rest sharedClient] GET:path parameters:nil success:success error:error];
}

@end

Location はアプリケーションのルート オブジェクトであり、頻繁に更新するように指示されます。UI インタラクション、イベント、またはデータのデシリアライゼーションのいずれかを介して、サーバーからより多くのデータを取得するために refreshApplications が実行されます。その間、JSON である API にデータを取得して送信するために、アプリケーションで他の要求とイベントが進行中です。他のエンドポイントへのこれらの GET 呼び出しの一部は、応答データをいじっているようです。

質問

  1. これは、AFNetworking でどのように発生する可能性がありますか?
  2. AFNetowrking のせいにするのは早すぎますか? また、応答を交差させている可能性のあるシステム内の他の場所を探す必要がありますか? Amazon でホストされている負荷分散されたバックエンドがあります。
  3. これはエンドポイントの問題ですか?
  4. この問題をより適切にデバッグして再現するにはどうすればよいですか? ランダムなタイミングでのみ発生し、複製するのは非常に困難です。クラッシュすることを期待して、アプリケーションを継続的に実行および再実行する必要があります。
  5. xcode を使用してこの呼び出し/クラッシュをバックトレースするために使用できる高度なデバッグ手法はありますか?
4

2 に答える 2

1

非 HTTPS および HTTPS トラフィックをデバッグするには、mitmproxyを使用します。

すべてのパッケージを検査し、再送信することもできます。これにより、実際に何が起こっているのか、バックエンドに問題があるのか​​、それとも AFNetworking にバグがあるのか​​を確認できます。

クールな副作用として、mitmproxyは完全に無料で、MIT ライセンスの下でオープンソース化されています。彼らの Web サイトには、iOS に特化した便利なチュートリアルがいくつかあります。

于 2013-06-19T14:25:34.773 に答える