mirror of
https://github.com/Instagram/IGListKit
synced 2026-05-24 01:38: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;
|
- (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
|
### Enhancements
|
||||||
|
|
||||||
- Added `shouldSelectItemAtIndex:` to `IGListSectionController` . [dirtmelon](https://github.com/dirtmelon)
|
- Added `shouldSelectItemAtIndex:` to `IGListSectionController` . [dirtmelon](https://github.com/dirtmelon)
|
||||||
|
|
|
||||||
|
|
@ -62,19 +62,6 @@ NS_SWIFT_NAME(ListAdapterUpdater)
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, assign) BOOL preferItemReloadsForSectionReloads;
|
@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.
|
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 sectionMovesAsDeletesInserts = _sectionMovesAsDeletesInserts;
|
||||||
@synthesize singleItemSectionUpdates = _singleItemSectionUpdates;
|
@synthesize singleItemSectionUpdates = _singleItemSectionUpdates;
|
||||||
@synthesize preferItemReloadsForSectionReloads = _preferItemReloadsForSectionReloads;
|
@synthesize preferItemReloadsForSectionReloads = _preferItemReloadsForSectionReloads;
|
||||||
@synthesize allowsBackgroundReloading = _allowsBackgroundReloading;
|
|
||||||
@synthesize allowsReloadingOnTooManyUpdates = _allowsReloadingOnTooManyUpdates;
|
@synthesize allowsReloadingOnTooManyUpdates = _allowsReloadingOnTooManyUpdates;
|
||||||
@synthesize experiments = _experiments;
|
@synthesize experiments = _experiments;
|
||||||
|
|
||||||
|
|
@ -40,7 +39,6 @@
|
||||||
|
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_transactionBuilder = [IGListUpdateTransactionBuilder new];
|
_transactionBuilder = [IGListUpdateTransactionBuilder new];
|
||||||
_allowsBackgroundReloading = YES;
|
|
||||||
_allowsReloadingOnTooManyUpdates = YES;
|
_allowsReloadingOnTooManyUpdates = YES;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
|
@ -91,7 +89,6 @@
|
||||||
.sectionMovesAsDeletesInserts = _sectionMovesAsDeletesInserts,
|
.sectionMovesAsDeletesInserts = _sectionMovesAsDeletesInserts,
|
||||||
.singleItemSectionUpdates = _singleItemSectionUpdates,
|
.singleItemSectionUpdates = _singleItemSectionUpdates,
|
||||||
.preferItemReloadsForSectionReloads = _preferItemReloadsForSectionReloads,
|
.preferItemReloadsForSectionReloads = _preferItemReloadsForSectionReloads,
|
||||||
.allowsBackgroundReloading = _allowsBackgroundReloading,
|
|
||||||
.allowsReloadingOnTooManyUpdates = _allowsReloadingOnTooManyUpdates,
|
.allowsReloadingOnTooManyUpdates = _allowsReloadingOnTooManyUpdates,
|
||||||
.experiments = _experiments,
|
.experiments = _experiments,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
[NSString stringWithFormat:@"sectionMovesAsDeletesInserts: %@", IGListDebugBOOL(self.sectionMovesAsDeletesInserts)],
|
[NSString stringWithFormat:@"sectionMovesAsDeletesInserts: %@", IGListDebugBOOL(self.sectionMovesAsDeletesInserts)],
|
||||||
[NSString stringWithFormat:@"singleItemSectionUpdates: %@", IGListDebugBOOL(self.singleItemSectionUpdates)],
|
[NSString stringWithFormat:@"singleItemSectionUpdates: %@", IGListDebugBOOL(self.singleItemSectionUpdates)],
|
||||||
[NSString stringWithFormat:@"preferItemReloadsForSectionReloads: %@", IGListDebugBOOL(self.preferItemReloadsForSectionReloads)],
|
[NSString stringWithFormat:@"preferItemReloadsForSectionReloads: %@", IGListDebugBOOL(self.preferItemReloadsForSectionReloads)],
|
||||||
[NSString stringWithFormat:@"allowsBackgroundReloading: %@", IGListDebugBOOL(self.allowsBackgroundReloading)],
|
|
||||||
[NSString stringWithFormat:@"allowsReloadingOnTooManyUpdates: %@", IGListDebugBOOL(self.allowsReloadingOnTooManyUpdates)]
|
[NSString stringWithFormat:@"allowsReloadingOnTooManyUpdates: %@", IGListDebugBOOL(self.allowsReloadingOnTooManyUpdates)]
|
||||||
];
|
];
|
||||||
[debug addObjectsFromArray:IGListDebugIndentedLines(options)];
|
[debug addObjectsFromArray:IGListDebugIndentedLines(options)];
|
||||||
|
|
|
||||||
|
|
@ -95,13 +95,6 @@ typedef NS_ENUM (NSInteger, IGListBatchUpdateTransactionMode) {
|
||||||
// disables multiple performBatchUpdates: from happening at the same time
|
// disables multiple performBatchUpdates: from happening at the same time
|
||||||
self.state = IGListBatchUpdateStateQueuedBatchUpdate;
|
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];
|
[self _diff];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ typedef struct {
|
||||||
BOOL sectionMovesAsDeletesInserts;
|
BOOL sectionMovesAsDeletesInserts;
|
||||||
BOOL singleItemSectionUpdates;
|
BOOL singleItemSectionUpdates;
|
||||||
BOOL preferItemReloadsForSectionReloads;
|
BOOL preferItemReloadsForSectionReloads;
|
||||||
BOOL allowsBackgroundReloading;
|
|
||||||
BOOL allowsReloadingOnTooManyUpdates;
|
BOOL allowsReloadingOnTooManyUpdates;
|
||||||
IGListExperiment experiments;
|
IGListExperiment experiments;
|
||||||
} IGListUpdateTransactationConfig;
|
} IGListUpdateTransactationConfig;
|
||||||
|
|
|
||||||
|
|
@ -533,7 +533,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test_whenCollectionViewNotInWindow_andBackgroundReloadFlag_isSetNO_diffHappens {
|
- (void)test_whenCollectionViewNotInWindow_andBackgroundReloadFlag_isSetNO_diffHappens {
|
||||||
self.updater.allowsBackgroundReloading = NO;
|
|
||||||
[self.collectionView removeFromSuperview];
|
[self.collectionView removeFromSuperview];
|
||||||
|
|
||||||
id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(IGListAdapterUpdaterDelegate)];
|
id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(IGListAdapterUpdaterDelegate)];
|
||||||
|
|
@ -557,57 +556,6 @@
|
||||||
[mockDelegate verify];
|
[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 {
|
- (void)test_whenReloadBatchedWithUpdate_thatCompletionBlockStillExecuted {
|
||||||
IGSectionObject *object = [IGSectionObject sectionWithObjects:@[@0, @1, @2]];
|
IGSectionObject *object = [IGSectionObject sectionWithObjects:@[@0, @1, @2]];
|
||||||
self.dataSource.sections = @[object];
|
self.dataSource.sections = @[object];
|
||||||
|
|
|
||||||
|
|
@ -413,7 +413,6 @@
|
||||||
|
|
||||||
IGListCollectionViewLayout *layout = [[IGListCollectionViewLayout alloc] initWithStickyHeaders:NO topContentInset:0 stretchToEdge:NO];
|
IGListCollectionViewLayout *layout = [[IGListCollectionViewLayout alloc] initWithStickyHeaders:NO topContentInset:0 stretchToEdge:NO];
|
||||||
self.collectionView = [[UICollectionView alloc] initWithFrame:self.frame collectionViewLayout:layout];
|
self.collectionView = [[UICollectionView alloc] initWithFrame:self.frame collectionViewLayout:layout];
|
||||||
[(IGListAdapterUpdater *)self.adapter.updater setAllowsBackgroundReloading:NO];
|
|
||||||
self.adapter.experiments |= IGListExperimentInvalidateLayoutForUpdates;
|
self.adapter.experiments |= IGListExperimentInvalidateLayoutForUpdates;
|
||||||
|
|
||||||
[self setupWithObjects:@[
|
[self setupWithObjects:@[
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue