问题
I need to draw text on a background thread to save it as an image.
I'm doing
UIGraphicsPushContext()
[NSString drawInRect:]
UIGraphicsPopContext()
The code works fine, but sometimes it crashes in drawInRect when I'm also drawing on the main thread at the same time.
I tried to use NSAttributedString as suggested here: UIStringDrawing methods don't seem to be thread safe in iOS 6. But [NSAttributedString drawInRect:] doesn't seem to render anything on my background thread for some reason. Main thread seems to work fine though.
I've been thinking of using Core Text, but looks like Core Text also has similar problem: CoreText crashes when run in multiple threads
Is there a thread safe way to draw text?
UPDATE: If I run this code, it almost immediately crashes in drawInRect with EXC_BAD_ACCESS:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0);
UIFont* font = [UIFont systemFontOfSize:14.0f];
for (int i = 0; i < 100000000; i++) {
[@"hello" drawInRect:CGRectMake(0, 0, 100, 100) withFont:font];
}
UIGraphicsEndImageContext();
});
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0);
UIFont* font = [UIFont systemFontOfSize:12.0f];
for (int i = 0; i < 100000000; i++) {
[@"hello" drawInRect:CGRectMake(0, 0, 100, 100) withFont:font];
}
UIGraphicsEndImageContext();
If I remove UIFont and just draw text without font it works fine.
UPDATE: This only seems to crash on iOS 6.1, but seems to work fine on iOS 7.1.
回答1:
Since iOS6 (might have been earlier) you can use those methods on a different thread, as long as you have created a new context using UIGraphicsBeginImageContext... on that same thread.
The drawRect: method defaults to the current context of their own thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0);
UIFont* font = [UIFont systemFontOfSize:26];
NSString* string = @"hello";
NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:string attributes:@{NSFontAttributeName:font}];
[attributedString drawInRect:CGRectMake(0, 0, 100, 100)];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[UIImagePNGRepresentation(image) writeToFile:@"/testImage.png" atomically:YES];
});
Run that on the simulator and it will output the result to the root directory of your hard drive.
回答2:
According to Apple's NSString UIKit Additions Reference, [NSString drawInRect:...]
is one of those methods that MUST be called from your app's main thread (look at the "Overview" section of that document). It says:
The methods described in this class extension must be used from your app’s main thread.
Then again, anything that updates the UI should always be on the main thread... certainly there are some things that could potentially be on background threads (e.g. including string and image drawing -- see this related question and answers).
And lastly, other people have reported problems trying to draw on background threads using "UIGraphicsPushContext" (at least with iOS 5.X), so that still may be a problem with iOS 6.
来源:https://stackoverflow.com/questions/23728994/how-do-i-draw-text-on-a-background-thread-in-ios