我有一个PDF文档,它在删除,裁剪,旋转等页面上有一些操作。
所以当我点击删除按钮并点击保存(当前主题:主线程)
时-(void)save
{
// -- process deleted pages first
for( NSString* deletedPageId in self.deletedPageIdList )
{
[self.pdfCoordinator removePageWithId:deletedPageId];
}
// -- wait for all pages to delete before continuing
[self.pdfCoordinator waitUntilAllOperationsAreFinished];
// few lines of code after this should run only when the above code finishes its execution
// code to save the changes in the pdf to managedObject context
}
removePageWithId的代码:
- (void) removePageWithId:(NSString*)pageId
{
NRMPDFOperation *op = [[NRMPDFOperation alloc] initWithNRMPDF:self
pageId:pageId
selector:@selector(removePageOpImpl:)
args:nil];
[self addOperation:op];
[op release];
}
上面的代码创建了一个操作,并将其添加到每次删除页面的操作队列中
removePageOpImpl的代码:
- (NSError *)removePageOpImpl:(NRMPDFOperation *)op
{
NSError* error = [self loadDocument];
if( !error )
{
NSUInteger index = [self pageIndexForId:[op pageId]];
if( index < [self pageCount] )
{
[[self pdfDocument] removePageAtIndex:index];
[[self mutablePageIdList] removeObjectAtIndex:index];
[self updatePageLabelsFromIndex:index];
[self updateChangeCount:NSChangeDone];
self.contentsChanged = YES;
}
else
{
// TODO: error
}
}
return error;
}
在removePageOpImpl:方法中的代码行 [[self pdfDocument] removePageAtIndex:index];在主线程上内部执行一些任务(但是我们让主线程等到这个操作完成)。这导致死锁。
我尝试在removePageOpImpl中执行代码:在异步调度队列中避免死锁。贝洛是那个代码
- (NSError *)removePageOpImpl:(NRMPDFOperation *)op
{
NSError* error = [self loadDocument];
if( !error )
{
NSUInteger index = [self pageIndexForId:[op pageId]];
if( index < [self pageCount] )
{
dispatch_async(dispatch_get_main_queue(), ^{
[[self pdfDocument] removePageAtIndex:index];
[[self mutablePageIdList] removeObjectAtIndex:index];
[self updatePageLabelsFromIndex:index];
[self updateChangeCount:NSChangeDone];
self.contentsChanged = YES;
});
}
else
{
// TODO: error
}
}
return error;
}
现在我摆脱了僵局。但另一个问题是将上面的代码放到异步块中,这个代码应该在此任务执行之前运行,因为我的应用程序没有按预期运行。
waitUntilAllOperationsAreFinished方法中的代码
- (void) addOperation:(NRMPDFOperation*)operation
{
[_operationSet addObject:operation];
[[self operationQueue] addOperation:operation];
}
- (void) waitUntilAllOperationsAreFinished
{
[[self operationQueue] waitUntilAllOperationsAreFinished];
}
- (NSOperationQueue *)operationQueue
{
if( !_operationQueue )
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
_operationQueue = queue;
}
return _operationQueue;
}
这是原始Save方法的样子:
- (void)saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void *)contextInfo
{
// TODO: don't just ignore saveOperation
__block BOOL success = YES;
__block NSError *error = nil;
/* write back field changes */
if ([item hasChangesInEditFieldsetFor:@"values"] )
{
//Some code
}
if( self.isPDFEdited )
{
// -- process deleted pages first
for( NSString* deletedPageId in self.deletedPageIdList )
{
[self.itemPDFCoordinator removePageWithId:deletedPageId];
}
// -- wait for all pages to delete before continuing
[self.itemPDFCoordinator waitUntilAllOperationsAreFinished];
// -- replace the search text any pages we deleted
if( [self.deletedPageIdList count] )
{
[self.item setValue:[self.editPDF string] forKeyPath:@"dict.values._searchText"];
}
NSMutableDictionary* originalRotations = [NSMutableDictionary dictionaryWithCapacity:
[self.itemPDFCoordinator pageCount]];
for( NSString* pageId in self.itemPDFCoordinator.pageIdList )
{
NSInteger rotation = [[self.itemPDFCoordinator pageForId:pageId] rotation];
[originalRotations setObject:[NSNumber numberWithInteger:rotation] forKey:pageId];
}
// -- now process actions on remaining pages (crop, rotate, and convert to b&w)
BOOL didCropAnyPages = NO;
NSMutableArray* convertToBwJobs = [NSMutableArray array];
for( NSString* pageId in [self.pageActionDict allKeys] )
{
NSArray* actions = [self.pageActionDict objectForKey:pageId];
for( NSDictionary* action in actions )
{
NSNumber* rotationNumber = [action objectForKey:@"rotation"];
NSValue* cropRectVal = [action objectForKey:@"cropRect"];
NSNumber* convertToBlackAndWhite = [action objectForKey:@"convertToBlackAndWhite"];
if( rotationNumber )
{
[self.itemPDFCoordinator rotateByDegrees:[rotationNumber integerValue]
forPageWithID:pageId];
}
else if( cropRectVal )
{
[self.itemPDFCoordinator setNormalizedBounds:[cropRectVal rectValue]
forBox:kPDFDisplayBoxCropBox
forPageWithID:pageId];
// -- set a flag so we know to recrop the entire document
didCropAnyPages = YES;
}
else if( [convertToBlackAndWhite boolValue] )
{
NSUInteger pageIndex = [self.itemPDFCoordinator pageIndexForId:pageId];
NRMJob* job = [NRMAppJobFactory convertToBlackAndWhiteJobForItem:self.item
pageIndex:pageIndex];
[convertToBwJobs addObject:job];
}
}
}
// -- reapply crop box to any cropped pages
if( didCropAnyPages )
{
[self.itemPDFCoordinator applyCropBoxToAllPages];
}
[self.itemPDFCoordinator waitUntilAllOperationsAreFinished];
for( NRMJob* job in convertToBwJobs )
{
if( ![[self.masterDocument docjob] addJob:job forItem:self.item error:&error] )
[NSApp presentError:error];
else
[job waitUntilDone];
}
// -- make sure document attributes are updated
NSDictionary *docDict = [self.itemPDFCoordinator documentAttributes];
NSDictionary *newDict = [(NRMItem *)item updateDocumentAttributes:docDict];
if (![newDict isEqualToDictionary:docDict])
[self.itemPDFCoordinator setDocumentAttributes:newDict];
[self.itemPDFCoordinator waitUntilAllOperationsAreFinished];
// -- check if we need to reprocess any pages
for( NSString* pageId in self.itemPDFCoordinator.pageIdList )
{
NSInteger oldRotation = [[originalRotations objectForKey:pageId] integerValue];
NSInteger newRotation = [[self.itemPDFCoordinator pageForId:pageId] rotation];
if( oldRotation != newRotation )
{
// -- if it's an image page and we already have OCR data for it, we should reanalyze
NSUInteger pageIndex = [self.itemPDFCoordinator pageIndexForId:pageId];
BOOL isPageImage = [self.itemPDFCoordinator isImagePageAtIndex:pageIndex DPI:NULL];
if( isPageImage && [item OCRDataForPageIndex:pageIndex] )
{
NRMJob* job = [NRMAppJobFactory reprocessPageJobForItem:self.item
pageIndex:pageIndex];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reanalyzeJobFinished:)
name:kNRMJobFinishedNotification
object:job];
success = [[self.masterDocument docjob] addJob:job forItem:self.item error:&error];
if( !success )
{
if( error )
[self presentError:error];
}
//goto bail;
}
}
}
//Force Save of PDF to Disk
[self.itemPDFCoordinator setManagedObjectContext:[item managedObjectContext]];
[self.itemPDFCoordinator saveChanges];
}
if( success )
{
[self updateChangeCount:NSChangeCleared];
if( self.isPDFEdited )
{
// !CLEARSPLIT! please do not remove this comment
[self.item setValue:nil forKeyPath:@"dict.values._splitId"];
if( ![self loadPDFForItem:item error:&error] )
goto bail;
}
}
bail:
if( error )
[self presentError:error];
if( delegate )
{
/* signature:
- (void)document:(NSDocument *)document didSave:(BOOL)didSaveSuccessfully contextInfo:(void *)contextInfo;
*/
objc_msgSend( delegate, didSaveSelector, self, (error ? NO : YES), contextInfo );
}
}
任何人都可以建议我如何摆脱这个问题。
答案 0 :(得分:0)
我建议指定一个额外的完成操作,而不是调用waitUntilAllOperationsAreFinished
(阻塞一个线程),你可以根据其他操作完成操作:
-(void)save {
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// whatever you want to do when it's done
}];
for (NSString* deletedPageId in self.deletedPageIdList) {
NSOperation *operation = [self.pdfCoordinator removePageWithId:deletedPageId];
[completionOperation addDependency:operation];
}
// add this completionOperation to a queue, but it won't actually start until the other operations finish
[queue addOperation:completionOperation];
}
显然,这假设您更改removePageWithId
以返回它添加到队列中的操作:
- (NSOperation *)removePageWithId:(NSString*)pageId {
NRMPDFOperation *operation = [[NRMPDFOperation alloc] initWithNRMPDF:self
pageId:pageId
selector:@selector(removePageOpImpl:)
args:nil];
[self addOperation:operation];
[operation release];
return operation;
}
这样,您不会阻止任何线程等待操作完成,而只是指定完成操作后要执行的操作。