我使用NSOperation的子类使用亚马逊的iOS SDK(v1.3.2)将大文件上传到AWS S3。这一切都很好,但一些beta测试者遇到死锁(iOS 5.1.1)。结果是,计划操作的NSOperationQueue被阻止,因为一次只允许一个操作运行。问题是我不能重现这个问题,而beta测试者每次都会遇到这个问题。
由于AWS iOS SDK的工作原理,操作非常复杂。但是,据我所知,根据我的测试,该问题与AWS iOS SDK无关。操作的主要方法粘贴在下面。操作的主要方法的想法基于this Stack Overflow question。
- (void)main {
// Operation Should Terminate
_operationShouldTerminate = NO;
// Notify Delegate
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate operation:self isPreparingUploadWithUuid:self.uuid];
});
// Increment Network Activity Count
[self incrementNetworkActivityCount];
// Verify S3 Credentials
[self verifyS3Credentials];
while (!_operationShouldTerminate) {
if ([self isCancelled]) {
_operationShouldTerminate = YES;
} else {
// Create Run Loop
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
// Decrement Network Activity Count
[self decrementNetworkActivityCount];
NSLog(@"Operation Will Terminate");
}
完成分段上传的方法将布尔值_operationShouldTerminate
设置为YES
以终止操作。这种方法看起来像这样。
- (void)finalizeMultipartUpload {
// Notify Delegate
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate operation:self didFinishUploadingUploadWithUuid:self.uuid];
});
// Operation Should Terminate
_operationShouldTerminate = YES;
NSLog(@"Finalize Multipart Upload");
}
最终的日志语句将打印到控制台,但main方法中的while循环似乎不会退出,因为操作的main方法中的最终日志语句不会打印到控制台。因此,将阻止调度操作的操作队列,并且不会执行任何计划的操作。
操作的isFinished
方法只返回_operationShouldTerminate
,如下所示。
- (BOOL)isFinished {
return _operationShouldTerminate;
}
很奇怪,while循环没有退出,更奇怪的是它不会发生在我自己的任何测试设备(iPhone 3GS,iPad 1和iPad 3)上。非常感谢任何帮助或指示。
答案 0 :(得分:2)
问题的解决方案既复杂又简单。我错误地假设操作的方法和委托回调是在同一个线程上执行的,也就是调用操作main
方法的线程。情况并非总是如此。
即使在我的测试和我的设备(iPhone 3GS)中都是如此,这就是我自己没有遇到问题的原因。但是,我的beta测试人员使用了具有多核处理器(iPhone 4 / 4S)的设备,这导致一些代码在与调用操作main
方法的线程不同的线程上执行。
结果是在错误的线程上_operationShouldTerminate
方法中修改了finalizeMultipartUpload
。这反过来意味着while
方法的main
循环未正确退出,导致操作死锁。
简而言之,解决方案是在调用_operationShouldTerminate
方法的同一线程上更新main
。这将正确退出while
循环并退出操作。
答案 1 :(得分:1)
您的代码存在许多问题,我可以提供两种解决方案:
1)阅读Apple Concurrency Programming Guide中的并发NSOperations。要使runLoop保持“活动”,您必须添加端口或计划计时器。主循环应该包含一个自动释放池,因为你可能没有(请参阅同一个备忘录中的内存管理)。您需要实现KVO以让operationQueue知道您的操作何时完成。
2)或者,您可以采用small amount of field tested hardened code并重复使用它。该Xcode项目包含三个您感兴趣的类:一个ConcurrentOperation文件,它可以很好地完成您在上面尝试完成的任务。 Webfetcher.m类显示如何子类化并发操作以从Web执行异步URL提取。 OperationsRunner是一个小助手文件,可以添加到任何类来管理操作队列(运行,取消,查询等)。以上所有代码都少于100行,为您提供了使代码正常工作的基础。 OperationsRunner.h文件也提供了“怎么办”。