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