Update UITextView during recursive function is running - objective-c

I have a view controller with a UITextView loaded into the frame. I want to update the text of the UITextView every time the function calls itself.
I attempt to update the UITextView on the main thread but it doesn't seem to set the text of the View UNTIL after the recursive function is done running.
'Maze.h' is the object that defines the protocol and 'MainViewController.m' is the delegate.
Heres the code for the controller:
'MainViewController.m'
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
Maze *aMaze = [[Maze alloc] initWithMaze:#"Maze.txt" andUpdate:true everyXSeconds:1];
[aMaze setDelegate:self];
if (![aMaze parseFile]) {
exit(2);
}
if ([aMaze solve:-1 y:-1 z:-1]){
NSLog(#"%#", [aMaze printMazeHorizontally]);
NSLog(#"Escapable: %#", [aMaze getMoveSequence]);
} else {
NSLog(#"Unescapable");
exit(1);
}
}
- (void)didMakeMove:(NSString *)maze {
NSLog(#"%#", maze);
dispatch_async(dispatch_get_main_queue(), ^{
[self.maze setText:maze];
});
}
'Maze.m'
- (BOOL)solve:(NSInteger)x y:(NSInteger)y z:(NSInteger)z
{
...
...
[self.delegate didMakeMove:self.printMazeVertically];
...
...
}
The UITextView just doesn't seem to update until -(BOOL)solve::: is done running. Which only updates once instead of multiple times.
Not sure why this is happening.
Any ideas on how to update the UITextView?
I thought that updating the UI should be done on the main thread?
Solution:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([aMaze solve:-1 y:-1 z:-1]){
NSLog(#"%#", [aMaze printMazeHorizontally]);
NSLog(#"Escapable: %#", [aMaze getMoveSequence]);
} else {
NSLog(#"Unescapable");
exit(1);
}
});

Drawing is performed later in the runloop run, or on the next runloop run. Thus, if you block the main thread while your recursion is running, UI will not update until after you end your recursion.
Consider changing your design. Move the taxing recursion to a background thread, and update the UI using GCD.

Related

Draw the contents of an array modified in the background

I have a background thread that runs and modifies the contents of a NSMutableArray within an object. This takes a long time to run (several hours) and I periodically want to draw the contents of an array within the drawRect of a NSView to check on progress and see the intermediate results.
My object has a protocol with a method called: didChange:
// How I start my background thread
[self performSelectorInBackground:#selector(startProcessing) withObject:nil];
- (void)startProcessing {
myObject.delegate = self;
[myObject start];
}
// My protocol implementation
- (void)myObjectDidChange:(myObjectClass *)sender {
[myView setNeedsDisplay:YES];
}
// My View's drawRect (pseudo code)
- (void)drawRect {
[myObject drawInContext:context];
}
All works, except that the NSMutableArray backing all this is being changed whilst the drawing takes place. How should I do this? Do I somehow pause the processing in the background thread whilst the update is taking place?
EDIT: This is the sort of display I am drawing (although much more complicated):
Any help appreciated.
If you are doing something in background thread and you want to update UI, its usually done on the main thread, so in your object did change you would do it, probably like this:
// My protocol implementation
- (void)myObjectDidChange:(myObjectClass *)sender {
dispatch_async(dispatch_get_main_queue(), ^{
[self drawRect]; //Or any drawing function you are trying to do
});
}
I have done it using NSLock to lock the outer loop of the start and the drawInContext methods. I am still not sure if this is the best approach and will not accept this answer for a few days in case there is a better answer out there.
- (void)start {
for(int i=0; i < MAX; i++) {
[self.updateLock lock];
....
[self.updateLock unlock];
}
}
- (void)drawInContext:(CGContextRef)context {
[self.updateLock lock];
...
[self.updateLock unlock];
}

Wait for two async methods to complete

I'd like to init a model, let the model do some async stuff and present a new viewcontroller once completed. But how do i wait for the two async methods to be completed and how do I setup the callback method?
Pseudocode
In my StartViewController.m:
-(void)openArticle
{
article = [Article initWithObject:someObject];
article.callback = changeView;
}
-(void)changeView
{
[self presentViewController:someController];
}
In my ArticleModel.m:
-(void)initWithObject:someObject
{
[self loadImage]
[self geoCode]
}
-(void)loadImage
{
runAsyncMethod: success:^() // This one is actually a AFNetworking setImageWithURLRequest
}
-(void)geoCode
{
runAnotherAsyncMethod: success:^() // This one is actually a geocodeAddressString operation
}
You can achieve this using dispatch_groups
- (void)initWithObject:(id)someObject
{
self = [super init];
if (self) {
self.dispatch_group = dispatch_group_create();
[self loadImage]
[self geoCode]
dispatch_group_notify(self.dispatch_group, dispatch_get_main_queue(), ^{
NSLog(#"Push new view controller");
});
}
return self;
}
- (void)loadImage
{
dispatch_group_enter(self.dispatch_group);
__weak __typeof(self) weakSelf = self;
runAsyncMethod: success:^{
__typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf.dispatch_group) {
dispatch_group_leave(strongSelf.dispatch_group); // You need to ensure that this is called in both success and failure
}
}
}
- (void)geoCode
{
dispatch_group_enter(self.dispatch_group);
__weak __typeof(self) weakSelf = self;
runAnotherAsyncMethod: success:^{
__typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf.dispatch_group) {
dispatch_group_leave(strongSelf.dispatch_group);
}
}
}
You do not wait. If you wait, it isn't asynchronous! You would be losing the entire point of asynchronous if you were to wait.
What you do is, when your success handler is called, you step out to the main thread (just in case you got called back on a background thread) and now do whatever you need to do. In other words, you just let your success handler get called whenever it happens to get called.
In your case, you might like to chain the things you want to do:
Call loadImage
In its callback, call geoCode
In its callback, step out to the main thread and present the new view controller.
You can use dispatch_group so that when a method is over, it just leaves the group. I use a similar code myself and it works like a charm.
- (void)initWithObject:someObject {
// Create a dispatch group
dispatch_group_t group = dispatch_group_create();
[self loadImageWithDispatchGroup:group];
[self geoCodeWithDispatchGroup:group];
// Here we wait for all the requests to finish
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Do whatever you need to do when all requests are finished
});
}
- (void)loadImageWithDispatchGroup:(dispatch_group_t)group {
dispatch_group_enter(group);
runAsyncMethod: success:^() // This one is actually a AFNetworking setImageWithURLRequest
// In your success or failure AFNetworking method, call this as soon as the request ended
dispatch_group_leave(group);
}
- (void)geoCodeWithDispatchGroup:(dispatch_group_t)group {
dispatch_group_enter(group);
runAnotherAsyncMethod: success:^() // This one is actually a geocodeAddressString operation
// In your success async geocode callback method, call this as soon as the request ended
dispatch_group_leave(group);
}
I do not known your needs but native GCD way to wait several asynch tasks is
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_barrier_async

NSThread Not Loading Selector Method

In the initialization method of a class I am declaring the thread as such:
NSThread* myThread = [[[NSThread alloc] initWithTarget:self selector:#selector(m_run_thread) object:nil] autorelease];
[myThread start];
I also have a boolean value which is set to NO. Later on in the code I set the boolean value to YES.
bool_run_progress_thread = YES;
The contents of the method m_run_thread is as follows:
-(void) m_run_thread
{
if (bool_run_progress_thread)
{
//do processing here
}
bool_run_progress_thread = NO;
}
The problem is that the method m_run_thread is never being accessed. What am I doing wrong?
P.S. I have also tried to set up the Thread using the following (and older)method:
[NSThread detachNewThreadSelector:#selector(m_run_thread)
toTarget:self
withObject:nil];
... but to no avail as well.
"...and I am only getting it to show once" Yes, that's exactly how it should be. After being started, a thread runs once from its start to its end (ignoring errors here for the moment), and having reached the end, the thread is essentially dead and gone.
If you want the thread to repeat its execution, you have to prepare for that yourself:
- (void) m_run_thread
{
for (;;)
{
if (bool_run_progress_thread)
{
//do processing here
bool_run_progress_thread = NO;
}
}
}
But there is still a lot wrong with this code: essentially, when run, the code forms a busy waiting loop. Assuming, that bool_run_progress_thread is only ever true for short periods of time, the background thread should be sleeping most of the time. Insead, if you try the code as its stands, it will instead consume CPU time (and lots of it).
A better approach to this would involve condition variables:
#class Whatsoever
{
NSCondition* cvar;
BOOL doProgress;
...
}
...
#end
and
- (void) m_run_thread
{
for (;;)
{
[cvar lock];
while (!doProgress)
{
[cvar wait];
}
doProgress = NO;
[cvar unlock];
... do work here ...
}
}
and in order to trigger the execution, you'd do:
- (void) startProgress
{
[cvar lock];
doProgress = YES;
[cvar signal];
[cvar unlock];
}
Doing things this way also takes care of another subtle problem: the visibility of the changes made to the global flag (your bool_run_progress_thread, my doProgess). Depending on the processor and its memory order, changes made without special protection might or might not become (ever) visible to other threads. This problem is taken care of by the NSCondition, too.

Synchronizing a Block within a Block?

I'm playing around with blocks in Objective-C, trying to come up with a reusable mechanism that will take an arbitrary block of code and a lock object and then execute the block of code on a new thread, synchronized on the provided lock. The idea is to come up with a simple way to move all synchronization overhead/waiting off of the main thread so that an app's UI will always be responsive.
The code I've come up with is pretty straightforward, it goes like:
- (void) executeBlock: (void (^)(void))block {
block();
}
- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
void(^syncBlock)() = ^{
#synchronized(lock) {
block();
}
};
[self performSelectorInBackground:#selector(executeBlock:) withObject:syncBlock];
}
So for example, you might have some methods that go like:
- (void) addObjectToSharedArray:(id) theObj {
#synchronized(array) {
[array addObject: theObj];
}
}
- (void) removeObjectFromSharedArray:(id) theObj {
#synchronized(array) {
[array removeObject: theObj];
}
}
Which works fine, but blocks the calling thread while waiting for the lock. These could be rewritten as:
- (void) addObjectToSharedArray:(id) theObj {
[self runAsyncBlock:^{
[array addObject: theObj];
} withLock: array];
}
- (void) removeObjectFromSharedArray:(id) theObj {
[self runAsyncBlock: ^{
[array removeObject: theObj];
} withLock:array];
}
Which should always return immediately, since only the background threads will compete over the lock.
The problem is, this code crashes after executeBlock: without producing any output, error message, crash log, or any other useful thing. Is there something fundamentally flawed in my approach? If not, any suggestions with respect to why this might be crashing?
Edit:
Interestingly, it works without crashing if I simply do:
- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
void(^syncBlock)() = ^{
#synchronized(lock) {
block();
}
};
syncBlock();
}
But of course this will block the calling thread, which largely defeats the purpose. Is it possible that blocks do not cross thread boundaries? I would think not, since that would largely defeat the purpose of having them in the first place.
remember to call [block copy] otherwise it is not correctly retained because block are created on stack and destroyed when exit scope and unless you call copy it will not move to heap even retain is called.
- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
block = [[block copy] autorelease];
void(^syncBlock)() = ^{
#synchronized(lock) {
block();
}
};
syncBlock = [[syncBlock copy] autorelease];
[self performSelectorInBackground:#selector(executeBlock:) withObject:syncBlock];
}

Pausing iteration of a for loop to wait for user input

I wrote a for loop which is iterating through an array of objects.
Now I am asking myself if it's possible to break the iteration of the loop until the user clicks on a button which calls a IBAction?
for (int i = 0; i < [array count]; i++) {
// do something with the object
// wait for action method called
// user clicked action so go on
}
You can adapt the code to fit your case. It basically "unrolls" the loop into multiple messages. Start the sequence with [self doItForIndex:[NSNumber numberWithInt:0]];
- (BOOL)canDoitForIndex:(NSNumber *)i {
// return YES if you want to go ahead
// (e.g. test a BOOL you set in response to the user tapping a button
}
- (void)waitForIndex:(NSNumber *)i {
if ([self canDoItForIndex:i]) {
// do anything to clean up for i
// then repeat for i+1:
[self doItForIndex:[NSNumber numberWithInt:[i intValue]+1]];
} else {
[self performSelector:_cmd withObject:i afterDelay:0.01f;
}
}
- (void)doItForIndex:(NSNumber *)i {
if ([i intValue] < lastIndex) {
// do what you have to do
[self waitForIndex:i];
}
// else you're done
}
Apple's NSRunLoop concept expects you to complete processing pretty quickly. If you tie up the main thread by waiting for something, nothing else in your app can happen. The above code breaks the "wait" into multiple message sends, and keeps your app responsive.
ODRM algorithm works very well.
I just changed this line :
[self performSelector:_cmd withObject:i afterDelay:0.01f];
with this :
[NSThread sleepForTimeInterval:0.25];
[NSThread detachNewThreadSelector:_cmd toTarget:self withObject:i];
As I had UI elements to be updated, it was better for we to force waiting to be in a background thread.