I'm working on an application in which I need to build some images in a background thread. At some point in the process, I need to get the text from a UITextView. If I call UITextview.text, I get the warning that my secondary threads shouldn't pester UIKit
That's all well and good, but I need the text and I can't figure out a reasonable way to get said text from the main thread.
My question is: Has anyone come up with a good way to get properties of UI elements from background threads or, alternatively, good ways to avoid doing so in the first place?
I threw this thing together and it does the trick, but it doesn't feel quite right:
@interface SelectorMap : NSObject
@property (nonatomic, strong) NSArray *selectors;
@property (nonatomic, strong) NSArray *results;
@end
@interface NSObject (Extensions)
- (NSArray *)getValuesFromMainThreadWithSelectors:(SEL)selector, ...;
- (void)performSelectorMap:(SelectorMap *)map;
@end
And the implementation:
#import "NSObject+Extensions.h"
@implementation SelectorMap
@synthesize selectors;
@synthesize results;
@end
@implementation NSObject (Extensions)
- (void)performSelectorMap:(SelectorMap *)map
{
NSMutableArray *results = [NSMutableArray arrayWithCapacity:map.selectors.count];
for (NSString *selectorName in map.selectors)
{
SEL selector = NSSelectorFromString(selectorName);
id result = [self performSelector:selector withObject:nil];
[results addObject:result];
}
map.results = results.copy;
}
- (NSArray *)getValuesFromMainThreadWithSelectors:(SEL)selector, ...
{
NSMutableArray *selectorParms = [NSMutableArray new];
va_list selectors;
va_start(selectors, selector);
for (SEL selectorName = selector; selectorName; selectorName = va_arg(selectors, SEL))
[selectorParms addObject:NSStringFromSelector(selectorName)];
va_end(selectors);
SelectorMap *map = [SelectorMap new];
map.selectors = selectorParms.copy;
[self performSelectorOnMainThread:@selector(performSelectorMap:) withObject:map waitUntilDone:YES];
return map.results;
}
@end
I call it like this:
NSArray *textViewProperties = [textView getValuesFromMainThreadWithSelectors:@selector(text), @selector(font), nil];
Getting the font doesn't give the same warning that getting the text does, but I figured it'd be best to be consistent.
I avoid any kind of a meta-programming as much as possible. It completely undermines the compilers ability to double-check your code, for starts, and it tends to be unreadable.
__block NSString* foo;
dispatch_sync(dispatch_get_main_queue(), ^{
foo = [textField ...];
});
Note if not using ARC, you'll likely want to copy
or retain
the string in the block and then release
or autorelease
in your local thread.
You can either use performSelector on main thread or you can use GCD to dispatch on main queue.
[self performSelectorOnMainThread:@selector(updateText:) withObject:nil waitUntilDone:YES];
GCD would look like:
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_sync(main, ^{
// read and assign here
});
Here's a related posts on the subject:
GCD, Threads, Program Flow and UI Updating
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With