[ASTextKitComponents] Make sure Main Thread Checker isn't triggered during background calculations #trivial (#612)

* Add unit test

* Make sure TextKit components can calculate size in background without upsetting Main Thread Checker
- Add a new thread-safe text view bounds.
- Temporary components stack doesn't have a text view so it can be safely deallocated off main.

* Add ASTextKitComponentsTextView

* Remove unnecessary change

* Fix minor mistake

* ASTextKitComponentsTextView has only 1 initializer

* Minor change

* Switch to atomic property

* Remove manual synthesization
This commit is contained in:
Huy Nguyen
2017-10-17 14:20:20 +01:00
committed by GitHub
parent 1e7d46196f
commit c12509e67a
4 changed files with 71 additions and 19 deletions

View File

@@ -20,6 +20,37 @@
#import <tgmath.h>
@interface ASTextKitComponentsTextView ()
@property (atomic, assign) CGRect threadSafeBounds;
@end
@implementation ASTextKitComponentsTextView
- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer
{
self = [super initWithFrame:frame textContainer:textContainer];
if (self) {
_threadSafeBounds = self.bounds;
}
return self;
}
- (void)setFrame:(CGRect)frame
{
ASDisplayNodeAssertMainThread();
[super setFrame:frame];
self.threadSafeBounds = self.bounds;
}
- (void)setBounds:(CGRect)bounds
{
ASDisplayNodeAssertMainThread();
[super setBounds:bounds];
self.threadSafeBounds = bounds;
}
@end
@interface ASTextKitComponents ()
// read-write redeclarations
@@ -27,9 +58,6 @@
@property (nonatomic, strong, readwrite) NSTextContainer *textContainer;
@property (nonatomic, strong, readwrite) NSLayoutManager *layoutManager;
// Indicates whether or not this object must be deallocated on main thread. Defaults to YES.
@property (nonatomic, assign) BOOL requiresMainThreadDeallocation;
@end
@implementation ASTextKitComponents
@@ -61,8 +89,6 @@
components.textContainer.lineFragmentPadding = 0.0; // We want the text laid out up to the very edges of the text-view.
[components.layoutManager addTextContainer:components.textContainer];
components.requiresMainThreadDeallocation = YES;
return components;
}
@@ -70,11 +96,9 @@
- (void)dealloc
{
if (_requiresMainThreadDeallocation) {
ASDisplayNodeAssertMainThread();
}
// Nil out all delegates to prevent crash
if (_textView) {
ASDisplayNodeAssertMainThread();
_textView.delegate = nil;
}
_layoutManager.delegate = nil;
@@ -88,10 +112,8 @@
// If our text-view's width is already the constrained width, we can use our existing TextKit stack for this sizing calculation.
// Otherwise, we create a temporary stack to size for `constrainedWidth`.
if (CGRectGetWidth(components.textView.bounds) != constrainedWidth) {
if (CGRectGetWidth(components.textView.threadSafeBounds) != constrainedWidth) {
components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX)];
// The temporary stack can be deallocated off main
components.requiresMainThreadDeallocation = NO;
}
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by -usedRectForTextContainer:).
@@ -112,9 +134,7 @@
// Always use temporary stack in case of threading issues
components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX)];
// The temporary stack can be deallocated off main
components.requiresMainThreadDeallocation = NO;
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by - usedRectForTextContainer:).
[components.layoutManager ensureLayoutForTextContainer:components.textContainer];