From 234b847beaac1f4d6064f55d1b5f166716da5c0d Mon Sep 17 00:00:00 2001 From: Kamo Spertsyan Date: Thu, 17 Oct 2024 12:29:38 +0300 Subject: [PATCH] Attempt to fix crashes and race conditions in User Properties manager --- .../QNUserPropertiesManager.m | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.m b/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.m index 33cb74e8..3df2dc2b 100644 --- a/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.m +++ b/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.m @@ -26,10 +26,10 @@ @interface QNUserPropertiesManager() @property (nonatomic) QNInMemoryStorage *inMemoryStorage; -@property (nonatomic, strong) NSMutableArray *completionBlocks; +@property (atomic, strong) NSMutableArray *completionBlocks; -@property (nonatomic, assign, readwrite) BOOL sendingScheduled; -@property (nonatomic, assign, readwrite) BOOL updatingCurrently; +@property (atomic, assign, readwrite) BOOL sendingScheduled; +@property (atomic, assign, readwrite) BOOL updatingCurrently; @property (nonatomic, assign, readwrite) NSUInteger retryDelay; @property (nonatomic, assign, readwrite) NSUInteger retriesCounter; @@ -94,25 +94,31 @@ - (void)forceSendProperties:(QONUserPropertiesEmptyCompletionHandler)completion return; } - [self.completionBlocks addObject:completion]; + @synchronized (self) { + [self.completionBlocks addObject:completion]; + } [self sendProperties:YES]; } - (void)sendPropertiesWithDelay:(NSUInteger)delay { - if (!_sendingScheduled) { - _sendingScheduled = YES; - __block __weak QNUserPropertiesManager *weakSelf = self; - [_backgroundQueue addOperationWithBlock:^{ - dispatch_async(dispatch_get_main_queue(), ^{ - [weakSelf performSelector:@selector(sendPropertiesInBackground) withObject:nil afterDelay:delay]; - }); - }]; + @synchronized (self) { + if (!_sendingScheduled) { + _sendingScheduled = YES; + __block __weak QNUserPropertiesManager *weakSelf = self; + [_backgroundQueue addOperationWithBlock:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf performSelector:@selector(sendPropertiesInBackground) withObject:nil afterDelay:delay]; + }); + }]; + } } } - (void)sendPropertiesInBackground { - _sendingScheduled = NO; + @synchronized (self) { + _sendingScheduled = NO; + } [self sendProperties]; } @@ -137,12 +143,16 @@ - (void)sendProperties:(BOOL)force { NSDictionary *properties = [self->_inMemoryStorage.storageDictionary copy]; if (!properties || ![properties respondsToSelector:@selector(valueForKey:)]) { - self->_updatingCurrently = NO; + @synchronized (self) { + self->_updatingCurrently = NO; + } return; } if (properties.count == 0) { - self->_updatingCurrently = NO; + @synchronized (self) { + self->_updatingCurrently = NO; + } return; } @@ -150,15 +160,19 @@ - (void)sendProperties:(BOOL)force { __block __weak QNUserPropertiesManager *weakSelf = self; [self.apiClient sendProperties:properties completion:^(NSDictionary * _Nullable dict, NSError * _Nullable error) { - weakSelf.updatingCurrently = NO; - NSArray *completions = [weakSelf.completionBlocks copy]; + NSArray *completions = @[]; + @synchronized (self) { + weakSelf.updatingCurrently = NO; + + completions = [weakSelf.completionBlocks copy]; + [weakSelf.completionBlocks removeAllObjects]; + } + for (QONUserPropertiesEmptyCompletionHandler storedCompletion in completions) { storedCompletion(); } - [weakSelf.completionBlocks removeAllObjects]; - if (error) { // copy of an existing array to prevent erasing properties set while the current request is in progress NSMutableDictionary *allProperties = [self.inMemoryStorage.storageDictionary mutableCopy];