-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ASCollectionView] Add delegate bridging and index space translation for missing UICollectionViewLayout properties. #440
Conversation
…for missing UICollectionViewLayout properties This is a first attempt at resolving #438. I know there is already one similar method to do indexPath conversions, and that the #define for accessing properties on the UICollectionViewFlowLayout should probably be moved somewhere else. Looking for feedback on the general direction here. In particular, I'm a bit surprised that so much has changed in how these calls occur, as I believe especially constrainedSizeForNodeAtIndexPath: is now called with inconsistent index path spaces (but was not before this PR landed in March: https://github.com/facebookarchive/AsyncDisplayKit/pull/3136/files) Since the impact of mixing the spaces is fairly serious (can cause crashes), I'm also wondering if I am misinterpreting some aspects of the code, or if maybe the crashing impact wasn't noticed yet.
🚫 CI failed with log |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it is indeed a problem. It's never safe to ask the data controller using an index path in UIKit space. The change in this PR looks safe to me.
I think we need to have new GitHub issues to track and discuss each of the followings:
- Have a mechanism in place that can detect and report illegal index-space crossings. I'm not sure, but it might be as simple as having an NSIndexPath wrapper that contains extra information regarding the index space.
- Do an audit on the current code base and fix problems similar to this.
- Discuss a new API for invalidating size ranges.
@Adlai-Holler Interested in your thoughts too.
🚫 CI failed with log |
Source/ASCollectionView.mm
Outdated
@@ -949,7 +955,8 @@ - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSe | |||
return [_dataController.visibleMap numberOfItemsInSection:section]; | |||
} | |||
|
|||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath | |||
- (CGSize)collectionView:(UICollectionView *)collectionView | |||
layout:(UICollectionViewLayout *)layout sizeForItemAtIndexPath:(NSIndexPath *)indexPath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you changed the parameter name from collectionViewLayout
to layout
, you need to update this line.
Source/ASCollectionView.mm
Outdated
if (supplementaryKind == nil) { | ||
if (indexPath == nil) { | ||
// In this case, the latest data no longer has the element, so we can't ask the delegate for its latest constrained size. | ||
// The element is still visible, but will be deleted soon. We can reuse the last-known constrainedSize for the element. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The element is still visible
"visible" is quite misleading here, and so does the "vibisbleMap" name. We should change it to "current" or something similar that indicates that the element is still recognized by UIKit objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. I agree that if we clarified the terminology here, and used it consistently everywhere (and we could even start putting it in method names, or comments at the start of methods), it would be easier to ensure correctness.
Ideas for "UIKit" space:
- View
- UIKit
- Committed
Ideas for "Latest" space:
- Source
- App
- DataSource
- Latest
I feel like there must be a better, more parallel set of names. One trouble with "current" is that you could imagine that being the current UIKit state, or the current App state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UIDataSourceTranslating
uses "Presentation." I kind of like that name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"DataSource" and "Presentation", or "Processed" and "Presenting"?
@nguyenhuy I'm really glad you mentioned the NSIndexPath wrapper idea. This is exactly what I had in mind, too. We should make the data types incompatible to cross the index-space boundary! @Adlai-Holler I'd like your feedback on this, but I think we should at least consider using a simple struct (exactly like ASIndexPath) for this purpose. Index path handling remains one of our biggest costs, including specifically main thread costs during large collection update application cycles that are the periods of time where we see frame drops. There might be an easier way to distinguish between the index spaces using NSIndexPath (although I'm hesitant to use an associated value, even a non-object integer). That said, it would be interesting to consider what the "best" way is to represent these values if we weren't tied to NSIndexPath. |
@appleguy @nguyenhuy I'm opposed to migrating to a struct because on 64-bit, NSIndexPath is a tagged pointer, and it's absolutely ubiquitous when dealing with UIKit. To prevent illegal index space crossings, I believe the best solution is for us to modify our APIs not to use index paths so often. For example, this delegate method should probably be changed to UIKit is restricted always to using NSIndexPaths because their API doesn't reify elements. We do. We have ASCollectionElement and ASCellNode which both serve as better identifiers for content than index paths, and from which we can always derive the appropriate index path in a desired space. |
@Adlai-Holler - ok, good points. Keeping NSIndexPath where we need to use index-based references at all. I think we are all supportive of using elements as widely as possible. However, we still need a way to keep the index spaces straight and physically prevent / assert against problems like the changing behavior of the constrainedSizeForNode: call (since this had been in the codebase for a while, it's an indication that some kind of assertion would've helped us catch it much sooner). Another challenge with relying on elements is that some APIs just need to use indexPaths. This includes UICollectionView passthrough. Although we shouldn't let that influence the node-based API, the technical requirements of keeping the index spaces straight are quite limited because of how great the changeSet / elementMap infrastructure is, allowing translation between them. So just like the tweaks in this patch, making sure the delegate gets sane values is more a matter of verifying correctness than a tough implementation challenge. Any feedback on this? Would an assertion to guard our assumptions be useful? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't beautiful, but it improves the reliability of using volatile data sources with flow layouts so I think we should roll with it. Thanks @appleguy for building this patch and following through on it.
Source/ASCollectionView.mm
Outdated
NSIndexPath *sectionIndexPath = [NSIndexPath indexPathForItem:0 inSection:section]; | ||
ASCollectionElement *element = [_dataController.visibleMap elementForItemAtIndexPath:sectionIndexPath]; | ||
NSIndexPath *pendingIndexPath = element ? [_dataController.pendingMap indexPathForElement:element] : nil; | ||
return pendingIndexPath; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be - (NSInteger)dataSourceSectionForSection:(NSInteger)section
? I could see not-checking-NSNotFound on the caller side being a problem. Not checking nil is roughly as likely to occur but slightly less likely to cause issues immediately.
Currently ASSection
objects are only created if the user implements the optional contextForSection:
API. We should instead create ASSections
unconditionally and use them as identifiers, similar to ASCollectionElement. That way looking up the indexes of sections between maps is easy!!
Source/ASCollectionView.mm
Outdated
if (supplementaryKind == nil) { | ||
if (indexPath == nil) { | ||
// In this case, the latest data no longer has the element, so we can't ask the delegate for its latest constrained size. | ||
// The element is still visible, but will be deleted soon. We can reuse the last-known constrainedSize for the element. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UIDataSourceTranslating
uses "Presentation." I kind of like that name.
Regarding preventing index space crossings, although I totally understand the reasons behind, I'm not so keen on changing the API. For the time being, adding assertions sounds fine, as long as those assertions always trigger, as opposed to trigger only after some race conditions (e.g different visible and pending maps). |
Model and Presentation could be good! It would mirror CALayer.
…On Fri, Jul 14, 2017 at 12:14 PM Huy Nguyen ***@***.***> wrote:
Regarding preventing index space crossings, although I totally understand
the reasons behind, I'm not so keen on changing the API. For the time
being, adding assertions sounds fine, as long as those assertions always
trigger, without requiring race conditions (e.g different visible and
pending maps) to occur beforehand.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#440 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAigA1j-PbU6JJHzFHvj9DpnL6e60Hp9ks5sN74zgaJpZM4OX35Q>
.
|
@nguyenhuy @Adlai-Holler I found another method that needs index-space conversion (layout:sizeForItem: itself). I have a fix for this locally, but haven't updated this patch yet. Thanks for the feedback here. I need to focus on fixing locally to address a release in flight. Feel free to continue landing diffs (although I would appreciate if we can hold off for a few days on any non-functional changes such as naming updates) and I will check which changes are still needed once rebasing. |
@nguyenhuy @Adlai-Holler I worked on this throughout the weekend, and it's pretty heavily tested now. If it passes your review, feel free to merge it. Thanks for your help! |
} else { | ||
sizeRange = [self dataController:_dataController constrainedSizeForSupplementaryNodeOfKind:supplementaryKind atIndexPath:indexPath]; | ||
return [node layoutThatFits:element.constrainedSize].size; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so we no longer reach out to the delegate for a size range but rely on element.constrainedSize
. This seems like a simpler approach 👍
It also means once a size range is returned by the delegate, it's no longer mutable by clients. They have to reload the element to change it, or request for a new API which simply exposes -[ASDataController relayoutNodes:nodesSizeChanged:]
. Until then, let's get this rolling.
…for missing UICollectionViewLayout properties. (TextureGroup#440) * [ASCollectionView] Add delegate bridging and index space translation for missing UICollectionViewLayout properties This is a first attempt at resolving TextureGroup#438. I know there is already one similar method to do indexPath conversions, and that the #define for accessing properties on the UICollectionViewFlowLayout should probably be moved somewhere else. Looking for feedback on the general direction here. In particular, I'm a bit surprised that so much has changed in how these calls occur, as I believe especially constrainedSizeForNodeAtIndexPath: is now called with inconsistent index path spaces (but was not before this PR landed in March: https://github.com/facebookarchive/AsyncDisplayKit/pull/3136/files) Since the impact of mixing the spaces is fairly serious (can cause crashes), I'm also wondering if I am misinterpreting some aspects of the code, or if maybe the crashing impact wasn't noticed yet. * [ASCollectionView] Cleanup and final changes for UIKit passthrough fixes.
This is a first attempt at resolving #438.
I know there is already one similar method to do indexPath conversions, and that the #define for
accessing properties on the UICollectionViewFlowLayout should probably be moved somewhere else.
Looking for feedback on the general direction here. In particular, I'm a bit surprised that so much
has changed in how these calls occur, as I believe especially constrainedSizeForNodeAtIndexPath: is
now called with inconsistent index path spaces (but was not before this PR landed in March:
https://github.com/facebookarchive/AsyncDisplayKit/pull/3136/files)
Since the impact of mixing the spaces is fairly serious (can cause crashes), I'm also wondering if
I am misinterpreting some aspects of the code, or if maybe the crashing impact wasn't noticed yet.