From 68eec6abd213ca8a7b9e6fca32ed0138e8d5fe56 Mon Sep 17 00:00:00 2001 From: turtledreams <62231246+turtledreams@users.noreply.github.com> Date: Fri, 15 Nov 2024 22:19:15 +0900 Subject: [PATCH] Concurrent modification fix --- CHANGELOG.md | 1 + CountlyViewTrackingInternal.m | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a61dfd3..2d8ae75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 24.7.8 * Mitigated an issue where visibility could have been wrongly assigned if a view was closed while going to background. (Experimental!) * Mitigated an issue where the user provided URLSessionConfiguration was not applied to direct requests +* Mitigated an issue where a concurrent modification error could have happen when starting multiple stopped views ## 24.7.7 * Changed the visibility tracking segmentation values to binary diff --git a/CountlyViewTrackingInternal.m b/CountlyViewTrackingInternal.m index 6b74a0c2..48bf365c 100644 --- a/CountlyViewTrackingInternal.m +++ b/CountlyViewTrackingInternal.m @@ -545,27 +545,33 @@ - (void)startStoppedViewsInternal { // Create an array to store keys for views that need to be removed NSMutableArray *keysToRemove = [NSMutableArray array]; - + NSMutableArray *keysToStart = [NSMutableArray array]; + + // Collect keys without modifying the dictionary [self.viewDataDictionary enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, CountlyViewData * _Nonnull viewData, BOOL * _Nonnull stop) { if (viewData.willStartAgain) { - NSString *viewID = [self startViewInternal:viewData.viewName customSegmentation:viewData.startSegmentation isAutoStoppedView:viewData.isAutoStoppedView]; - - // Retrieve the newly created viewData for the viewID - CountlyViewData* viewDataNew = self.viewDataDictionary[viewID]; - - // Copy the segmentation data from the old view to the new view - viewDataNew.segmentation = viewData.segmentation.mutableCopy; - - // Add the old view's ID to the array for removal later + [keysToStart addObject:key]; [keysToRemove addObject:viewData.viewID]; } }]; + // Start the collected views after enumeration + for (NSString *key in keysToStart) + { + CountlyViewData *viewData = self.viewDataDictionary[key]; + NSString *viewID = [self startViewInternal:viewData.viewName customSegmentation:viewData.startSegmentation isAutoStoppedView:viewData.isAutoStoppedView]; + + // Retrieve and update the newly created viewData + CountlyViewData *viewDataNew = self.viewDataDictionary[viewID]; + viewDataNew.segmentation = viewData.segmentation.mutableCopy; + } + // Remove the entries from the dictionary [self.viewDataDictionary removeObjectsForKeys:keysToRemove]; } + - (void)stopAllViewsInternal:(NSDictionary *)segmentation { // TODO: Should apply all the segmenation operations here at one place instead of doing it for individual view