0

I am using a UIDocument subclass which has the potential have its saveToURL method called from more than one thread. I have therefore encapsulated it in a wrapper function which I want to make thread safe:

- (void)saveWithCompletionBlock:(void(^)(TransactionDocumentReturnCode status))completion {
    @synchronized (self) {
        [self saveToURL:[self fileURL] forSaveOperation:UIDocumentSaveForOverwriting completionHandler:^(BOOL success){
            // Generate returncode depending on outcome of save operation
            completion(returncode);
        }];
    }
}

I am assuming that the call to [self saveToURL:...] will return immediately since the save operation itself occurs on a background thread, causing the lock to be released potentially before the save operation is completed. So, is there any way to keep other threads calling saveWithCompletionBlock: blocked until saveToURL's completion block has been called?

mashers
  • 1,009
  • 7
  • 22
  • Have a look at this answer for two ways of implementing this: http://stackoverflow.com/a/7649768/78496 – chedabob Mar 25 '16 at 18:04
  • Thank you @chedabob. Would using GCD or NSOperationQueue result in the subsequent calls to the save wrapper function being blocked until the save operation's completion block had finished? – mashers Mar 25 '16 at 18:21

1 Answers1

0

One potential solution I have just thought of is to add a property to my UIDocument subclass to indicate if a save operation is in progress, and if so to wait and then try again. Wrapping the checking and setting of this property in @synchronize should prevent two threads from simultaneously reading the value as false and believing they can save the document:

- (void)saveWithCompletionBlock:(void(^)(TransactionDocumentReturnCode status))completion {
    @synchronized(self){
        if (self.isSaving) {
            [self performSelector:@selector(saveWithCompletionBlock:) withObject:completion afterDelay:1.0];
            return;
        }

        self.isSaving = YES;
    }

    [self saveToURL:[self fileURL] forSaveOperation:UIDocumentSaveForOverwriting completionHandler:^(BOOL success){
        // Generate returncode depending on outcome of save operation
        completion(returncode);
        self.isSaving = NO;
    }];
}

I don't know if this would be sufficient or considered good multi-threading practice.

mashers
  • 1,009
  • 7
  • 22
  • The above solution seems to work well. Adding a call to `[NSObject cancelPreviousPerformRequestsWithTarget:self]` before `[self performSelector:...]` caused the app to wait until the end of the first save operation and then save just once again after it has completed. I would still welcome any feedback on this solution. – mashers Mar 25 '16 at 19:00