I'm writing an objective C program. Below I have included a method that I'm running after clicking on a button in that program. Also there is a stop button in that program and when someone clicks on the stop button I wanna stop execution of this method and bring the UI of the application back to it's normal state which was before running the method. Can anyone help me to do this?
-(JobStatus)beginUploadingTask{
void (^progressBlock)(void);
progressBlock = ^{
#try{
do {
// calls to some method
dispatch_async(dispatch_get_main_queue(), ^{
//execute some code
});
} while (index<fileSize);
}
#catch (NSException *ex) {
[self taskErrorWithMessage:#"Error in uploading your file. Please try again"];
return;
}
#finally {
NSLog(# "finally block executed");
}
};
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, progressBlock);
return TaskStateUploaded;
}
Related
I have a XCTTestClass that has an asynchronous setup method. It will take some amount of time (has to parse files, insert them in a bd, etc) and I want to make sure my tests only run after this setup is done.
How can I do this?
You can use semaphores to wait till you get the results back from your async call.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// Do your async call here
// Once you get the response back signal:
[self asyncCallWithCompletionBlock:^(id result) {
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
In your -setup method use either a semaphore as above or use dispatch_group. dispatch_group is my preferred approach.
#implementation XCTTestSubClass()
{
dispatch_group_t _dispatchGroup;
}
#end
-(id)init
{
_dispatchGroup = dispatch_group_create();
return [super init];
}
-(void)setup
{
dispatch_group_async(_dispatchGroup, dispatch_get_current_queue(), ^{
//your setup code here.
});
}
Then override -invokeTest and make sure the group blocks(setup) is done running.
-(void)invokeTest
{
dispatch_group_notify(group, dispatch_get_current_queue(), ^{
[super invokeTest];
});
}
This guarantees that the tests will run only after -setup is completed.
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.
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
I'm trying to use sleep in the below code when the NSStream Connection fails or if there's a Stream Error and tries to reconnect after sleep. The Sleep is working but it puts the Whole Application to Sleep.
I have started NSStream as a Thread, but when the NSStreamEvent is received, the handleEvent seems to be working as a Synchronous method.
Any ideas on using Sleep for this piece of Code ..? I just want the sleep to work for the Stream Thread alone.
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event
{
case NSStreamEventErrorOccurred:
{
NSError *streamErr = [stream streamError];
NSString *strErr = [streamErr localizedFailureReason];
[self CloseStream];
NSLog(#"Stream Error ::: %#",strErr);
//[NSThread sleepForTimeInterval : 15];
sleep(15);
[self Initialize];
[self OpenStream];
break;
}
case NSStreamEventEndEncountered:
{
NSLog(#"Connection Closed by the Server");
[self CloseStream];
usleep(15000);
[self Initialize];
[self OpenStream];
break;
}
}
You should use GCD (Grand Central Dispatch). Your code is being executed in the Background and your application doesn't freeze.
Read this: GCD Reference
Basically you create a queue and add a block of code, which is being executed in the background. Here's my code example
dispatch_queue_t backgroundQueue = dispatch_queue_create("some_identifier", NULL);
dispatch_async(backgroundQueue, ^(void) {
//do your background stuff
dispatch_sync(dispatch_get_main_queue(), ^{
//update the gui (if needed)
});
});
When the user clicks a button, an action is started but if the user quickly clicks the button 10 times, it will execute 10 times. I guess the disable doesn't take effect until control returns from the event.
- (IBAction)btnQuickCheckClick:(id)sender {
#try {
self.btnQuickCheck.enabled = NO ;
// Next line takes about 3 seconds to execute:
[self pollRouter] ;
}
#finally {
self.btnQuickCheck.enabled = YES ;
}
}
You can update the UI by running the run loop after disabling the button before polling:
- (IBAction)btnQuickCheckClick:(id)sender {
self.btnQuickCheck.enabled = NO;
// give some time for the update to take place
[self performSelector:#selector(pollRouterMethod) withObject:nil afterDelay:0.1];
}
- (void)pollRouterMethod {
#try {
[self pollRouter];
} #catch (NSException * e) { }
// re-enable the button
self.btnQuickCheck.enabled = YES;
}
Of course, this method is no substitute for running a time intensive task on another thread. For long tasks, multithreading is almost always the way to go.
another way to do this is with blocks :
Big Pro : you don't need to create an extra method :)
- (IBAction)btnQuickCheckClick:(id)sender {
//UI changes must be done in the main thread
self.btnQuickCheck.enabled = NO;
//do your thing in a background thread
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT ,0);
dispatch_async(queue, ^(){
#try {
//do your thing here
[self pollRouter];
} #catch (NSException * e) {
//handle the exception, if needed
} #finally {
//change to the main thread again and re-enable the UI
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^(){
self.btnQuickCheck.enabled = YES;
});
}
});
}
This will run pollRouter in a background thread. So if you are not modifying the UI or other non thread safe things in there you want to use this approach :) Otherwise go for #Alex's approach