Tritonal
Tritonal

Reputation: 831

Temporary NSTextView calls custom NSColor subclass, calling UI in background thread

I am measuring text block heights in a background thread. For some reason, if this happens when the application has just started, the thread makes a call to a NSColor subclass called DynamicColor which needs to be in main thread, as it checks if the app is in dark mode or not.

The class where the background thread is running should not have any reference to DynamicColor, but still, when the NSTextStorage is allocated, DynamicColor gets called. It causes an error for making UI calls from background task.

I have no idea how the NSTextStorage could be making call to some class it doesn't know to exist. Only my NSDocument subclass has referral to it, none of the other classes should be unaware of it.

Call to background thread in NSDocument:

- (void)updatePreviewAndUI:(bool)updateUI {         
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
        Preview *preview = [[PreviewCreator alloc] initWithScript:script];
    });
}

The background thread goes over numerous elements containing string data and calculates their heights.

+ (NSInteger)heightForString:(NSString *)string font:(NSFont *)font maxWidth:(NSInteger)maxWidth lineHeight:(CGFloat)lineHeight
{
    // The error occurs here
    NSTextStorage   *textStorage   = [[NSTextStorage alloc] initWithString:string attributes:@{NSFontAttributeName: font }];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(maxWidth, MAXFLOAT)];
    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];
    ...
}

Call to DynamicColor happens when NSTextStorage is allocated. I was able to avoid crashes by checking if we are on the main thread in DynamicColor, but I'm worried about the cause of the error.

DynamicColor is a NSColor subclass. Here are the relevant parts:

#define FORWARD( PROP, TYPE ) \
- (TYPE)PROP { return [self.effectiveColor PROP]; }

- (NSColor *)effectiveColor
{
    if (NSApp) {
        // Call to Application delegate causes the thread error
        if ([(ApplicationDelegate*)[NSApp delegate] isForcedLightMode]) {
            if (self.aquaColor != nil) return self.aquaColor;
        }
        else if ([(ApplicationDelegate*)[NSApp delegate] isForcedDarkMode]) {
            if (self.darkAquaColor != nil) return self.darkAquaColor;
        }
    }
}

I tried setting a plain NSColor as the NSTextContainer foreground color, but it didn't help. The error does not occur every time when running the app, and in a test environment I was unable to reproduce the problem. Also, it's only the first time a text container is allocated, when this happens.

Upvotes: 0

Views: 51

Answers (0)

Related Questions