-
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
Add Experimental Text Node Implementation #259
Conversation
🚫 CI failed with log |
Not sure if GitHub is messing with me, or this diff actually has 10k lines :P Can we somehow break it into chunks? And/or link against YYText (if you haven't done so)? |
@nguyenhuy My understanding is that this diff is really 10k lines :) We don't fully link against YYText as we don't need all of the components, furthermore it seems like the projects is kind of a dead state :/ |
Lol yep the diff is 10k lines. I think we shouldn't link to YYText since it's dead, since we don't need most of it, since we will need to fork it to make modifications (I've already found one, around the shadowing of truncated lines), and also because it's a hassle to our users. |
OK. So that's 10k lines we're gonna closely review now and maintain later. However, if the code quality and architecture is better than our current implementation, and if in the future we'll completely switch to it, it might actually turn out to be a net gain! |
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.
Jesus I hope I reviewed the right parts.
Source/ASTextNode.mm
Outdated
|
||
// They must call this before allocating any text nodes. | ||
ASDisplayNodeAssertFalse(_hasAllocatedNode); | ||
ASDisplayNodeAssert(_experimentOptions == 0, @"Can't call +setExperimentOptions: more than once."); |
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.
You sure can't it's in a dispatch_once
Source/Private/ASTextNode2.mm
Outdated
{ | ||
NSString *plainString = [[self.attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; | ||
if (plainString.length > 50) { | ||
plainString = [[plainString substringToIndex:50] stringByAppendingString:@"\u2026"]; |
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.
Super super nit, I assume this evaluates to …
, maybe just use the character?
Source/Private/ASTextNode2.mm
Outdated
// We discard the backing store and renderer to prevent the very large | ||
// memory overhead of maintaining these for all text nodes. They can be | ||
// regenerated when layout is necessary. | ||
[super clearContents]; // ASDisplayNode will set layer.contents = nil |
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.
Is this method necessary?
Source/Private/ASTextNode2.mm
Outdated
return layout.textBoundingSize; | ||
|
||
|
||
// ASTextKitRenderer *renderer = [self _rendererWithBoundsSlow:{.size = constrainedSize}]; |
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.
Delete?
|
||
// Accessiblity | ||
self.accessibilityLabel = attributedText.string; | ||
self.isAccessibilityElement = (length != 0); // We're an accessibility element by default if there is a string. |
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.
Probably not, but is there any way attributedText.length could be different than attributedText.string.length?
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.
No I don't think so.
textLayoutCache = [[NSCache alloc] init]; | ||
}); | ||
|
||
ASTextCacheValue *cacheValue = ({ |
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.
What happens below when a bunch of nodes with the same text all go through this path? The first one sets an empty cache value and the rest of them get it. Then they all 'cache miss' below and layout?
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.
Yep. It's an acceptable tradeoff, since (1) we will mostly be doing sequential passes on the same attributed string, not parallel ones and (2) the alternative would either be complex or amount to having all other text-layout work wait.
Source/Private/ASTextNode2.mm
Outdated
{ | ||
std::lock_guard<std::mutex> lock(cacheValue->_m); | ||
cacheValue->_layouts.push_front(std::make_tuple(container.size, layout)); | ||
if (cacheValue->_layouts.size() > 3) { |
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.
Is 3 a magic number? I wonder if there's value in having control of this. My guess is specific strings have layouts that would be worth caching and most have 0
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.
Is 3 a magic number?
Yes.
I wonder if there's value in having control of this. My guess is specific strings have layouts that would be worth caching and most have 0
No probably not. This cache is primarily intended to serve between layout passes and displays of the same text node. Any benefit that we get across the app is negligible (but great!).
Source/Private/ASTextNode2.mm
Outdated
std::lock_guard<std::mutex> lock(cacheValue->_m); | ||
cacheValue->_layouts.push_front(std::make_tuple(container.size, layout)); | ||
if (cacheValue->_layouts.size() > 3) { | ||
cacheValue->_layouts.pop_back(); |
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.
Eh, not sure how much it's worth optimizing, but it may be worth making this LRU as opposed to first layout loses.
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.
Yep, a couple lines up there is a TODO for this.
return _shadowColor; | ||
} | ||
|
||
- (void)setShadowColor:(CGColorRef)shadowColor |
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.
We should take this opportunity to make it a UIColor?
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.
No because this property is inherited from ASDisplayNode.
Source/Private/ASTextNode2.mm
Outdated
ASTextTruncationType yyType; | ||
switch (truncationMode) { | ||
case NSLineBreakByTruncatingHead: | ||
yyType = ASTextTruncationTypeStart; |
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.
Looks like the license at the top of this needs to give credit to yy?
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.
No this file uses code from YY but it's all our code.
@garrettmoon Thanks for reviewing this. What should we do about the license text? Two issues we need to deal with:
Or we can remove the header danger rule. I don't think it's adding much value right now. |
Generated by 🚫 Danger |
In order to keep moving, I'm going to land this with the YYText-header warnings and the one from KittenNode.mm intact. @garrettmoon Let's run a pass updating the license headers in the sample code, or update or disable the license header rule altogether. |
🚫 CI failed with log |
🚫 CI failed with log |
https://paper.dropbox.com/doc/ASTextNode-Experimental-Replacement-bswIlZJjmjDj1fJjwegzH
I'd like to get this reviewed and merged so that we can start experimenting with it in Pinterest and YouTube, even though the experimental implementation is missing some features.
@garrettmoon @nguyenhuy @maicki @appleguy
cc #221 & #227