mirror of
https://github.com/Instagram/IGListKit
synced 2026-05-06 06:58:26 +00:00
goodbye allowsBackgroundReloading
Summary: Originally, `allowsBackgroundReloading` was added to improve performance, but ironically, it's causing lots of performance issues among other issues. * Performance: Looking back, it's not too surprising that it causes perf issues. We're falling back to a full `-reloadData` if the view is not in the window, which can happen pretty often. For example, if a view-controller is within a `UINavigationController` stack but not on top, or within a `UITabBarController`. Because a full `-reloadData` will re-query the cells and re-create the entire layout, it's going to be more expensive than an incremental update via `-performUpdatesAnimated`. The proof is in the data and we have a few examples where this flag was the cause of significant UI stalls. * Bugs: Because we might reload cells often, it can create strange animation artifacts. Specifically, it was breaking the `UIView` snapshots just before a transition, like the new zoom animator. Overall, we ended disabling this feature and I think most apps will be in the same boat. But what if this flag does improve my app's performance? * File an issue and lets chat! I'd be curious to understand why that's the case. If a full `-reloadData` is more performant than an incremental `-performUpdatesAnimated`, than something odd is happening and I don't think this flag is the right solution. Reviewed By: joetam Differential Revision: D25884777 fbshipit-source-id: c4626a52082ef4c7b7300b21077529f26c551e70
This commit is contained in:
parent
4ca6e9d0a6
commit
032e1b0b83
8 changed files with 2 additions and 78 deletions
|
|
@ -36,6 +36,8 @@ The changelog for `IGListKit`. Also see the [releases](https://github.com/instag
|
|||
- (void)performDataSourceChange:(IGListDataSourceChangeBlock)block;
|
||||
```
|
||||
|
||||
- Removed `allowsBackgroundReloading` from `IGListAdapterUpdater` because it's causing performance issues and other bugs. [Maxime Ollivier](https://github.com/maxolls) (tbd)
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Added `shouldSelectItemAtIndex:` to `IGListSectionController` . [dirtmelon](https://github.com/dirtmelon)
|
||||
|
|
|
|||
|
|
@ -62,19 +62,6 @@ NS_SWIFT_NAME(ListAdapterUpdater)
|
|||
*/
|
||||
@property (nonatomic, assign) BOOL preferItemReloadsForSectionReloads;
|
||||
|
||||
/**
|
||||
A flag indicating whether this updater should skip diffing and simply call
|
||||
`reloadData` for updates when the collection view is not in a window. The default value is `YES`.
|
||||
|
||||
Default is YES.
|
||||
|
||||
@note This will result in better performance, but will not generate the same delegate
|
||||
callbacks. If using a custom layout, it will not receive `prepareForCollectionViewUpdates:`.
|
||||
|
||||
@warning On iOS < 8.3, this behavior is unsupported and will always be treated as `NO`.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL allowsBackgroundReloading;
|
||||
|
||||
/**
|
||||
If there's more than 100 diff updates, fallback to using `reloadData` to avoid stalling the main thread.
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
@synthesize sectionMovesAsDeletesInserts = _sectionMovesAsDeletesInserts;
|
||||
@synthesize singleItemSectionUpdates = _singleItemSectionUpdates;
|
||||
@synthesize preferItemReloadsForSectionReloads = _preferItemReloadsForSectionReloads;
|
||||
@synthesize allowsBackgroundReloading = _allowsBackgroundReloading;
|
||||
@synthesize allowsReloadingOnTooManyUpdates = _allowsReloadingOnTooManyUpdates;
|
||||
@synthesize experiments = _experiments;
|
||||
|
||||
|
|
@ -40,7 +39,6 @@
|
|||
|
||||
if (self = [super init]) {
|
||||
_transactionBuilder = [IGListUpdateTransactionBuilder new];
|
||||
_allowsBackgroundReloading = YES;
|
||||
_allowsReloadingOnTooManyUpdates = YES;
|
||||
}
|
||||
return self;
|
||||
|
|
@ -91,7 +89,6 @@
|
|||
.sectionMovesAsDeletesInserts = _sectionMovesAsDeletesInserts,
|
||||
.singleItemSectionUpdates = _singleItemSectionUpdates,
|
||||
.preferItemReloadsForSectionReloads = _preferItemReloadsForSectionReloads,
|
||||
.allowsBackgroundReloading = _allowsBackgroundReloading,
|
||||
.allowsReloadingOnTooManyUpdates = _allowsReloadingOnTooManyUpdates,
|
||||
.experiments = _experiments,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
[NSString stringWithFormat:@"sectionMovesAsDeletesInserts: %@", IGListDebugBOOL(self.sectionMovesAsDeletesInserts)],
|
||||
[NSString stringWithFormat:@"singleItemSectionUpdates: %@", IGListDebugBOOL(self.singleItemSectionUpdates)],
|
||||
[NSString stringWithFormat:@"preferItemReloadsForSectionReloads: %@", IGListDebugBOOL(self.preferItemReloadsForSectionReloads)],
|
||||
[NSString stringWithFormat:@"allowsBackgroundReloading: %@", IGListDebugBOOL(self.allowsBackgroundReloading)],
|
||||
[NSString stringWithFormat:@"allowsReloadingOnTooManyUpdates: %@", IGListDebugBOOL(self.allowsReloadingOnTooManyUpdates)]
|
||||
];
|
||||
[debug addObjectsFromArray:IGListDebugIndentedLines(options)];
|
||||
|
|
|
|||
|
|
@ -95,13 +95,6 @@ typedef NS_ENUM (NSInteger, IGListBatchUpdateTransactionMode) {
|
|||
// disables multiple performBatchUpdates: from happening at the same time
|
||||
self.state = IGListBatchUpdateStateQueuedBatchUpdate;
|
||||
|
||||
// if the collection view isn't in a visible window, skip diffing and batch updating. execute all transition blocks,
|
||||
// reload data, execute completion blocks, and get outta here
|
||||
if (self.config.allowsBackgroundReloading && self.collectionView.window == nil) {
|
||||
[self _reload];
|
||||
return;
|
||||
}
|
||||
|
||||
[self _diff];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ typedef struct {
|
|||
BOOL sectionMovesAsDeletesInserts;
|
||||
BOOL singleItemSectionUpdates;
|
||||
BOOL preferItemReloadsForSectionReloads;
|
||||
BOOL allowsBackgroundReloading;
|
||||
BOOL allowsReloadingOnTooManyUpdates;
|
||||
IGListExperiment experiments;
|
||||
} IGListUpdateTransactationConfig;
|
||||
|
|
|
|||
|
|
@ -533,7 +533,6 @@
|
|||
}
|
||||
|
||||
- (void)test_whenCollectionViewNotInWindow_andBackgroundReloadFlag_isSetNO_diffHappens {
|
||||
self.updater.allowsBackgroundReloading = NO;
|
||||
[self.collectionView removeFromSuperview];
|
||||
|
||||
id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(IGListAdapterUpdaterDelegate)];
|
||||
|
|
@ -557,57 +556,6 @@
|
|||
[mockDelegate verify];
|
||||
}
|
||||
|
||||
- (void)test_whenCollectionViewNotInWindow_andBackgroundReloadFlag_isDefaultYES_fallbackToReload {
|
||||
[self.collectionView removeFromSuperview];
|
||||
|
||||
id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(IGListAdapterUpdaterDelegate)];
|
||||
self.updater.delegate = mockDelegate;
|
||||
|
||||
[mockDelegate setExpectationOrderMatters:YES];
|
||||
[[mockDelegate expect] listAdapterUpdater:self.updater willReloadDataWithCollectionView:self.collectionView isFallbackReload:YES];
|
||||
[[mockDelegate expect] listAdapterUpdater:self.updater didReloadDataWithCollectionView:self.collectionView isFallbackReload:YES];
|
||||
|
||||
XCTestExpectation *expectation = genExpectation;
|
||||
NSArray *to = @[
|
||||
[IGSectionObject sectionWithObjects:@[]]
|
||||
];
|
||||
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock]
|
||||
animated:NO
|
||||
sectionDataBlock:[self dataBlockFromObjects:self.dataSource.sections toObjects:to]
|
||||
applySectionDataBlock:self.applySectionDataBlock
|
||||
completion:^(BOOL finished) {
|
||||
[expectation fulfill];
|
||||
}];
|
||||
waitExpectation;
|
||||
[mockDelegate verify];
|
||||
}
|
||||
|
||||
- (void)test_whenCollectionViewNotInWindow_andBackgroundReloadFlag_isDefaultYES_andDataSourceWasSetToNilBefore_fallbackToReload {
|
||||
[self.collectionView removeFromSuperview];
|
||||
|
||||
id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(IGListAdapterUpdaterDelegate)];
|
||||
self.updater.delegate = mockDelegate;
|
||||
|
||||
[mockDelegate setExpectationOrderMatters:YES];
|
||||
[[mockDelegate expect] listAdapterUpdater:self.updater willReloadDataWithCollectionView:self.collectionView isFallbackReload:YES];
|
||||
[[mockDelegate expect] listAdapterUpdater:self.updater didReloadDataWithCollectionView:self.collectionView isFallbackReload:YES];
|
||||
|
||||
XCTestExpectation *expectation = genExpectation;
|
||||
NSArray *to = @[
|
||||
[IGSectionObject sectionWithObjects:@[]]
|
||||
];
|
||||
self.collectionView.dataSource = nil;
|
||||
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock]
|
||||
animated:NO
|
||||
sectionDataBlock:[self dataBlockFromObjects:self.dataSource.sections toObjects:to]
|
||||
applySectionDataBlock:self.applySectionDataBlock
|
||||
completion:^(BOOL finished) {
|
||||
[expectation fulfill];
|
||||
}];
|
||||
waitExpectation;
|
||||
[mockDelegate verify];
|
||||
}
|
||||
|
||||
- (void)test_whenReloadBatchedWithUpdate_thatCompletionBlockStillExecuted {
|
||||
IGSectionObject *object = [IGSectionObject sectionWithObjects:@[@0, @1, @2]];
|
||||
self.dataSource.sections = @[object];
|
||||
|
|
|
|||
|
|
@ -413,7 +413,6 @@
|
|||
|
||||
IGListCollectionViewLayout *layout = [[IGListCollectionViewLayout alloc] initWithStickyHeaders:NO topContentInset:0 stretchToEdge:NO];
|
||||
self.collectionView = [[UICollectionView alloc] initWithFrame:self.frame collectionViewLayout:layout];
|
||||
[(IGListAdapterUpdater *)self.adapter.updater setAllowsBackgroundReloading:NO];
|
||||
self.adapter.experiments |= IGListExperimentInvalidateLayoutForUpdates;
|
||||
|
||||
[self setupWithObjects:@[
|
||||
|
|
|
|||
Loading…
Reference in a new issue