【发布时间】:2013-06-15 21:41:04
【问题描述】:
我在我的代码中发现了一个非常奇怪且随机的问题,我无法追踪。从 AFNetworking JSON 请求方法返回时,我的数据模型初始化方法出现崩溃。当应用程序确实崩溃时,我可以退回调用堆栈以调试 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
位置是应用程序中的根对象,它会被告知经常刷新。无论是通过 UI 交互、事件还是数据反序列化,refreshApplications 都会执行以从服务器获取更多数据。同时,应用程序中正在进行的其他请求和事件以获取和发送数据到 API 是 JSON。其中一些对其他端点的 GET 调用似乎会混淆响应数据。
问题
- AFNetworking 怎么会发生这种情况?
- 我是否过于急于责备 AFNetowrking,我是否应该在我的系统中寻找其他可能与响应发生冲突的地方?我确实有一个在 Amazon 上托管的负载平衡后端。
- 这是端点问题吗?
- 如何更好地调试和重现此问题?它只是随机出现,很难复制。我必须不断运行并重新运行应用程序,希望它崩溃。
- 是否有任何高级调试技术可用于使用 xcode 回溯此调用/崩溃?
【问题讨论】:
-
您可以启用异常断点,该断点可能会显示崩溃将出现的确切行,还会显示崩溃将出现的线程。此外,查看崩溃日志也会很有帮助。
-
我已经把它打开了,这就是我知道它在哪里崩溃的方式。
-
您是否尝试过使用 Postman 或任何其他 REST 客户端来仔细检查服务器的数据是否始终一致?我一直在使用 AFNetworking,但从未见过它跨请求。另外,您可以发布任何崩溃报告吗?
-
顺便说一句,您的成功回调需要 NSObject *obj,您将其转换为 NSArray,如果来自 AFNetworking 的 responseObject 实际上是 JSON,它可能是 NSDictionary。所以这也可能引起一些奇怪。只是一个想法。此外,应用程序的 initWithData 方法需要 (Category *)category 并且您正在传递 [[SmartAppCategory alloc] init]。
-
您是否尝试过将您的 appsForLocation 方法直接硬编码到 refreshApplications 中(仅用于测试目的)?我的第一个想法是可能会通过共享模型资源进行交叉处理。至少可以排除这种可能性。
标签: ios objective-c debugging objective-c-blocks afnetworking