0

Previously I've been able to use the following code to place attributed text into an NSTextView field

[[self->content textStorage] appendAttributedString:contentString];

However, upon updating to the Xcode 6 Beta to develop for Yosemetie, what used to work now gives me this error:

2014-08-26 11:06:06.635 JaxWire[59482:1765389] An uncaught exception was raised
2014-08-26 11:06:06.636 JaxWire[59482:1765389] Modifications to the layout engine must not be performed from a background thread.

and a whole much more globety gloop that I don't think has much use.

To give context as to how it works, a user enters text in a field, triggers a method called GO in the class InterfaceManager which then runs the following code to spawn a new thread ProcessQuery in the AppDelegate class, which is where I am attempting to change the content of NSTextView with an attributed string

[NSThread detachNewThreadSelector:@selector(processQuery:) toTarget:self withObject:query];

It's worth noting that this works if I use a standard string by executing [self->content setString:@"String"]; but when it comes to using an attributed string, it doesn't work.

How would I go about fixing this problem?

Thanks in advance! :)

SteppingHat
  • 1,199
  • 4
  • 19
  • 50
  • 2
    The error is pretty clear, you are trying to modify the layout engine on a background thread. You need to do it on the main thread. – Dima Aug 26 '14 at 02:24
  • @Dima But this code used to work before with Xcode 5. Besides, I don't know how to push it to the main thread... – SteppingHat Aug 26 '14 at 02:33
  • I'll write some sample code in an answer. – Dima Aug 26 '14 at 02:47

2 Answers2

2

Try editing the string on the main thread instead:

dispatch_async(dispatch_get_main_queue(), ^()
{
    [[self->content textStorage] appendAttributedString:contentString];
});
Dima
  • 23,484
  • 6
  • 56
  • 83
  • Careful. Without knowing the context of the surrounding code, you can't be sure that doing that asynchronously is safe. For example, what if `contentString` is a mutable attributed string and is mutated just after the original call to `-appendAttributedString:`? With your change, that mutation is in a race with the dispatch. Sadly, you also can't know that using `dispatch_sync()` would be safe, either, because it might deadlock. :( – Ken Thomases Aug 26 '14 at 03:09
  • Nah surrounding code wasn't super complex in terms of how things are synchronously or asynchronously, which means the code worked brilliantly! Thanks for your help :) – SteppingHat Aug 26 '14 at 05:19
1

it has nothing at all to do with Xcode. You are explicitly asking for this to be done on a background thread by using [NSThread detachNewThreadSelector], and as has been pointed out in comments, this is not allowed. You have never been "allowed" to modify the UI from a background thread, but often you could do so without noticeable problems. Yosemite is likely just enforcing this more strictly than previous versions of OS X.

I suppose the reason it works with setString: is because setString: probably doesn't immediately call the layout engine and rather the next drawing pass on the main thread does that.

you either have to not detach processQuery to a background thread, or have processQuery call appendAttributedString on the main thread using something like:

[[self->content textStorage] performSelector:@selector(appendAttributedString:) withObject: contentString]

or using GCD dispatch_async() with the main thread's queue dispatch_get_main_queue()

Brad Allred
  • 7,323
  • 1
  • 30
  • 49