From 257f8249bf9458707be2d509ff00fe8d77aae7e7 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 16 Oct 2017 11:01:13 -0400 Subject: [PATCH] Cull cached cell media outside a load window. // FREEBIE --- .../ConversationViewController.m | 70 ++++++++++++++++++- .../ConversationView/ConversationViewItem.h | 2 +- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 502dbbaec..81f1af6de 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -165,7 +165,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { @property (nonatomic, nullable) OWSAudioAttachmentPlayer *audioAttachmentPlayer; @property (nonatomic, nullable) NSUUID *voiceMessageUUID; -@property (nonatomic) NSTimer *readTimer; +@property (nonatomic, nullable) NSTimer *readTimer; +@property (nonatomic, nullable) NSTimer *cellMediaCachesTimer; @property (nonatomic) ConversationHeaderView *navigationBarTitleView; @property (nonatomic) UILabel *navigationBarTitleLabel; @property (nonatomic) UILabel *navigationBarSubtitleLabel; @@ -526,6 +527,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { self.hasClearedUnreadMessagesIndicator = NO; [self.dynamicInteractions clearUnreadIndicatorState]; } + [self clearCellMediaCaches]; } - (void)applicationWillResignActive:(NSNotification *)notification @@ -943,6 +945,69 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { - (void)cancelReadTimer { [self.readTimer invalidate]; + self.readTimer = nil; +} + +- (void)startCellMediaCachesTimerIfNecessary +{ + if (self.cellMediaCachesTimer) { + return; + } + self.cellMediaCachesTimer = [NSTimer weakScheduledTimerWithTimeInterval:3.f + target:self + selector:@selector(auditCellMediaCaches) + userInfo:nil + repeats:NO]; +} + +- (void)auditCellMediaCaches +{ + [self.cellMediaCachesTimer invalidate]; + self.cellMediaCachesTimer = nil; + + NSIndexPath *_Nullable firstVisibleIndexPath = nil; + NSIndexPath *_Nullable lastVisibleIndexPath = nil; + + for (NSIndexPath *indexPath in self.collectionView.indexPathsForVisibleItems) { + if (!firstVisibleIndexPath || firstVisibleIndexPath.row > indexPath.row) { + firstVisibleIndexPath = indexPath; + } + if (!lastVisibleIndexPath || lastVisibleIndexPath.row < indexPath.row) { + lastVisibleIndexPath = indexPath; + } + } + + if (!firstVisibleIndexPath || !lastVisibleIndexPath) { + [self clearCellMediaCaches]; + return; + } + + // Only retain the cached cell media for N cells at a time. + const NSInteger kCellMediaCacheWindowSize = 24; + NSInteger centerRow = (firstVisibleIndexPath.row + lastVisibleIndexPath.row) / 2; + // Determine the first and law rows (inclusive) of the cached cell media load window. + // Note these row values may not correspond to actual rows in the collection view. + // + // Always retain the cached cell media for any visible cells. + NSInteger firstLoadWindowRow = MIN(firstVisibleIndexPath.row, centerRow - kCellMediaCacheWindowSize / 2); + NSInteger lastLoadWindowRow = MAX(lastVisibleIndexPath.row, centerRow + kCellMediaCacheWindowSize / 2); + + // Cull cached cell media outside the load window. + for (ConversationViewItem *viewItem in self.viewItems) { + if (viewItem.row < firstLoadWindowRow || viewItem.row > lastLoadWindowRow) { + viewItem.cachedCellMedia = nil; + } + } +} + +- (void)clearCellMediaCaches +{ + [self.cellMediaCachesTimer invalidate]; + self.cellMediaCachesTimer = nil; + + for (ConversationViewItem *viewItem in self.viewItems) { + viewItem.cachedCellMedia = nil; + } } - (void)viewDidAppear:(BOOL)animated @@ -986,6 +1051,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { [self saveDraft]; [self markVisibleMessagesAsRead]; [self cancelVoiceMemo]; + [self clearCellMediaCaches]; self.isUserScrolling = NO; } @@ -3531,6 +3597,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [self updateLastVisibleTimestamp]; + + [self startCellMediaCachesTimerIfNecessary]; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index 1827a7a6d..75212d1d8 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -90,7 +90,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); // media. This cache is volatile and will get evacuated based // on scroll state, so that we only retain state for a sliding // window of cells that are almost on-screen. -@property (nonatomic) id cachedCellMedia; +@property (nonatomic, nullable) id cachedCellMedia; @property (nonatomic) BOOL didCellMediaFailToLoad; // TODO: