Использование @synchronized с синхронными операциями

Я использую подкласс UIDocument, у которого есть потенциал, чтобы его метод saveToURL вызывался из более чем одного потока. Поэтому я инкапсулировал его в функцию-оболочку, которую я хочу сделать потокобезопасной:

- (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);
        }];
    }
}

Я предполагаю, что вызов [self saveToURL:...] вернется немедленно, так как сама операция сохранения происходит в фоновом потоке, в результате чего блокировка может быть освобождена до завершения операции сохранения. Итак, есть ли способ заблокировать другие потоки, вызывающие saveWithCompletionBlock:, пока не будет вызван блок завершения saveToURL?


person mashers    schedule 25.03.2016    source источник
comment
Взгляните на этот ответ для двух способов реализации: stackoverflow.com/a/7649768/78496   -  person chedabob    schedule 25.03.2016
comment
Спасибо @chedabob. Приведет ли использование GCD или NSOperationQueue к блокировке последующих вызовов функции-оболочки сохранения до завершения блока завершения операции сохранения?   -  person mashers    schedule 25.03.2016


Ответы (1)


Одно потенциальное решение, о котором я только что подумал, состоит в том, чтобы добавить свойство в мой подкласс UIDocument, чтобы указать, выполняется ли операция сохранения, и если да, то подождать, а затем повторить попытку. Обертывание проверки и установки этого свойства в @synchronize должно помешать двум потокам одновременно прочитать значение как ложное и полагать, что они могут сохранить документ:

- (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;
    }];
}

Я не знаю, будет ли этого достаточно или будет считаться хорошей практикой многопоточности.

person mashers    schedule 25.03.2016
comment
Вышеупомянутое решение, кажется, работает хорошо. Добавление вызова [NSObject cancelPreviousPerformRequestsWithTarget:self] перед [self performSelector:...] привело к тому, что приложение ждало окончания первой операции сохранения, а затем сохранялось только один раз после ее завершения. Я бы по-прежнему приветствовал любые отзывы об этом решении. - person mashers; 25.03.2016