AutorÃa | Ultima modificación | Ver Log |
// Copyright 2019 Google//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.#import "Crashlytics/Shared/FIRCLSOperation/FIRCLSCompoundOperation.h"#import "Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation_Private.h"#define FIRCLS_DISPATCH_QUEUES_AS_OBJECTS OS_OBJECT_USE_OBJC_RETAIN_RELEASEconst NSUInteger FIRCLSCompoundOperationErrorCodeCancelled = UINT_MAX - 1;const NSUInteger FIRCLSCompoundOperationErrorCodeSuboperationFailed = UINT_MAX - 2;NSString *const FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors =@"com.google.firebase.crashlytics.FIRCLSCompoundOperation.error.user-info-key.underlying-"@"errors";static NSString *const FIRCLSCompoundOperationErrorDomain =@"com.google.firebase.crashlytics.FIRCLSCompoundOperation.error";static char *const FIRCLSCompoundOperationCountingQueueLabel ="com.google.firebase.crashlytics.FIRCLSCompoundOperation.dispatch-queue.counting-queue";@interface FIRCLSCompoundOperation ()@property(strong, nonatomic, readwrite) NSOperationQueue *compoundQueue;@property(assign, nonatomic) NSUInteger completedOperations;@property(strong, nonatomic) NSMutableArray *errors;#if FIRCLS_DISPATCH_QUEUES_AS_OBJECTS@property(strong, nonatomic) dispatch_queue_t countingQueue;#else@property(assign, nonatomic) dispatch_queue_t countingQueue;#endif@end@implementation FIRCLSCompoundOperation- (instancetype)init {self = [super init];if (!self) {return nil;}_compoundQueue = [[NSOperationQueue alloc] init];_completedOperations = 0;_errors = [NSMutableArray array];_countingQueue =dispatch_queue_create(FIRCLSCompoundOperationCountingQueueLabel, DISPATCH_QUEUE_SERIAL);return self;}#if !FIRCLS_DISPATCH_QUEUES_AS_OBJECTS- (void)dealloc {if (_countingQueue) {dispatch_release(_countingQueue);}}#endif- (void)main {for (FIRCLSFABAsyncOperation *operation in self.operations) {[self injectCompoundAsyncCompletionInOperation:operation];[self injectCompoundSyncCompletionInOperation:operation];[self.compoundQueue addOperation:operation];}}- (void)cancel {if (self.compoundQueue.operations.count > 0) {[self.compoundQueue cancelAllOperations];dispatch_sync(self.countingQueue, ^{[self attemptCompoundCompletion];});} else {for (NSOperation *operation in self.operations) {[operation cancel];}// we have to add the operations to the queue in order for their isFinished property to be set// to true.[self.compoundQueue addOperations:self.operations waitUntilFinished:NO];}[super cancel];}- (void)injectCompoundAsyncCompletionInOperation:(FIRCLSFABAsyncOperation *)operation {__weak FIRCLSCompoundOperation *weakSelf = self;FIRCLSFABAsyncOperationCompletionBlock originalAsyncCompletion = [operation.asyncCompletion copy];FIRCLSFABAsyncOperationCompletionBlock completion = ^(NSError *error) {__strong FIRCLSCompoundOperation *strongSelf = weakSelf;if (originalAsyncCompletion) {dispatch_sync(strongSelf.countingQueue, ^{originalAsyncCompletion(error);});}[strongSelf updateCompletionCountsWithError:error];};operation.asyncCompletion = completion;}- (void)injectCompoundSyncCompletionInOperation:(FIRCLSFABAsyncOperation *)operation {__weak FIRCLSCompoundOperation *weakSelf = self;void (^originalSyncCompletion)(void) = [operation.completionBlock copy];void (^completion)(void) = ^{__strong FIRCLSCompoundOperation *strongSelf = weakSelf;if (originalSyncCompletion) {dispatch_sync(strongSelf.countingQueue, ^{originalSyncCompletion();});}dispatch_sync(strongSelf.countingQueue, ^{[strongSelf attemptCompoundCompletion];});};operation.completionBlock = completion;}- (void)updateCompletionCountsWithError:(NSError *)error {dispatch_sync(self.countingQueue, ^{if (!error) {self.completedOperations += 1;} else {[self.errors addObject:error];}});}- (void)attemptCompoundCompletion {if (self.isCancelled) {[self finishWithError:[NSError errorWithDomain:FIRCLSCompoundOperationErrorDomaincode:FIRCLSCompoundOperationErrorCodeCancelleduserInfo:@{NSLocalizedDescriptionKey : [NSStringstringWithFormat:@"%@ cancelled", self.name]}]];self.asyncCompletion = nil;} else if (self.completedOperations + self.errors.count == self.operations.count) {NSError *error = nil;if (self.errors.count > 0) {error = [NSErrorerrorWithDomain:FIRCLSCompoundOperationErrorDomaincode:FIRCLSCompoundOperationErrorCodeSuboperationFaileduserInfo:@{FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors : self.errors}];}[self finishWithError:error];}}@end