Delegate methods are not calling when using separate thread - ios

I have used below code for do a synchronization with web server. It is working but UI freeze for a moment becasue getUnsyncTicketsFromServer method calling in main queue. I tried to do it in separate queue. Then it doesn't call delegate methods(requestReturnedData).
[NSTimer scheduledTimerWithTimeInterval:320.0 target:self selector:#selector(syncTickets) userInfo:nil repeats:YES];
-(void)syncTickets{
[self sendUnsyncedTickets];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self getUnsyncTicketsFromServer];
});
});
}
-(void)requestReturnedData:(NSData *)data{
}
edit --
-(void)getUnsyncTicketsFromServer{
ServiceConnector *serviceConnector = [[ServiceConnector alloc] init];
serviceConnector.delegate = self;
[serviceConnector getTicketsFromServer];
dataMethod = #"get";
}

#implementation YourClass () {
ServiceConnector *_serviceConnector;
}
...
-(void)getUnsyncTicketsFromServer {
_serviceConnector = [[ServiceConnector alloc] init];
_serviceConnector.delegate = self;
[_serviceConnector getTicketsFromServer];
dataMethod = #"get";
}
If you are using arc, I think your serviceConnector must be getting released, so store the object as an instance variable.

Related

Is it acceptable to daisy NSOperation main into super?

- (void)main
{
IDBAssert0(self.bestCapture.webpCandidate);
self.finished = NO;
self.executing = YES;
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
UIImage *possiblycorrupted = [UIImage imageWithWebPData:self.bestCapture.webpCandidate];
NSTimeInterval webpInterval = [NSDate timeIntervalSinceReferenceDate]-now;
NSDLog(#"it tooke %.2f sec to unpack webp", webpInterval);
self.microblinkCandidate = possiblycorrupted; // data superclass nsoperation processes
[super main];
}
first thing main in the base class does naturally is setting finished to no and executing to yes:
- (void)main
{
self.finished = NO;
self.executing = YES;
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
start = now;
CGSize size = [self.microblinkCandidate size];
IDBAssert0(size.width && size.height);
IDBAssert0(self.microblink);
// this starts async processing
[self.microblink processImage:self.microblinkCandidate
scanningRegion:CGRectMake(0.0, 0.0, 1.0, 1.0)
delegate:self];
while (![self isCancelled])
{
sleep(1);
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
if(now - start > 5) {
// #5677 microblink watchdog to detect hangs
[self cancel];
break;
}
}
[self done];
}
cause it's not an abstract and will be used on its own as well.
the loop is for debug/watchdog purposes only
in the normal operation it's not tripped an operation is done
if this callback:
- (void)scanningViewController: (UIViewController<PPScanningViewController>*)scanningViewController
didOutputResults:(NSArray*)results
{
if([results count]>0) {
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
NSDLog(#"found barcode in %.1fs", now - start);
self.microblinkSuccessHandler();
}else{
IDBAssert0(self.microblinkFailureHandler);
self.microblinkFailureHandler();
}
[self done];
}
is invoked when "processImage:" will have finished (in a timely fashion).
the very base class is
#implementation IDBAsynchronousOperation
#synthesize executing = _executing;
#synthesize finished = _finished;
-(BOOL)isFinished
{
return _finished;
}
- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:#"isFinished"];
_finished = finished;
[self didChangeValueForKey:#"isFinished"];
}
-(BOOL)isExecuting
{
return _executing;
}
- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:#"isExecuting"];
_executing = executing;
[self didChangeValueForKey:#"isExecuting"];
}
- (instancetype)init
{
self = [super init];
if (self) {
// self.completionBlock = ^{
// NSDLog(#"image barcode search has finished");
// };
IDBAssert0(sizeof(_executing)<2);
}
return self;
}
-(BOOL)isAsynchronous
{
return YES;
}
#end
You certainly can (and we often do) subclass your own concrete NSOperation subclass.
To make the base class subclassable, you want to make sure that you only perform self.executing = true once. Right now, the main in both the base class and the subclass do it, and you'll therefore be doing it twice. The typical solution is to pull it out of both of those main implementations and do it in start of the base class. Apple suggests that you do this stuff in start, anyway.
Thus having removed the self.finished and self.executing stuff from both main implementations, you can then implement start:
- (void)start {
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
[self main];
}
Note, you don't have to call self.finished = false when the operation is starting because that will send an unnecessary KVO.
An unrelated observation:
If you keep the while loop in the base class, I'd suggest exiting the loop if either [self isCancelled] or if the processImage delegate completion methods was called (perhaps you can update some state property to designate when that delegate method was called). Right now, if the processImage finishes before the timeout, it will keep the operation running for the full 5 seconds.
Personally, depending upon how processImage was designed, I probably be inclined excise the while loop entirely. You generally want to avoid any polling like this at all. I might, for example, put the [self done] in the appropriate delegate method and then set up a timer or dispatch_after for the timeout.
- (void)main {
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
start = now;
CGSize size = [self.microblinkCandidate size];
IDBAssert0(size.width && size.height);
IDBAssert0(self.microblink);
// this starts async processing
[self.microblink processImage:self.microblinkCandidate
scanningRegion:CGRectMake(0.0, 0.0, 1.0, 1.0)
delegate:self];
// cancel upon timeout
typeof(self) __weak weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
typeof(self) __strong strongSelf = weakSelf;
if ([strongSelf isExecuting]) {
[strongSelf cancel];
[strongSelf done]; // if canceling calls the delegate method that calls `done`, then you don't need this here
}
});
}

MBProgressHUD showAnimated: whileExecutingBlock: not working

I'm using a method for scanning Bluetooth device, which I import from another framework. The scanning method would take a while, and it'll block the GUI, which is what we never want to happen.
I'm also having MBProgressHud, trying to show a hud while scanning, but it's not working (hud not showing up). Any help?
Here's the code I'm currently using:
[hud showAnimated:YES whileExecutingBlock:^{
self.btDevices = [Util scanBT];
}];
Edit 1: Okay, so if I use this code, it'll still block my UI for a while, then all suddenly continue to run.
hud = [[MBProgressHUD alloc] initWithView:self.view];
hud.labelText = #"Now scanning";
hud.dimBackground = YES;
hud.opacity = 0.5;
[hud show:YES];
[hud hide:YES afterDelay:5.0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.001 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
self.btDevices = [Util scanBT];
});
Edit 2: Ok, I will post all my block of code:
hud = [[MBProgressHUD alloc] initWithView:[self getTopView:self.view]];
hud.labelText = #"Now scanning";
hud.dimBackground = YES;
hud.opacity = 0.5;
[hud showAnimated:YES whileExecutingBlock:^{
self.btDevices = [Util scanBT];
}];
dispatch_queue_t myqueue = dispatch_queue_create("queue", NULL);
dispatch_async(myqueue, ^{
//Whatever is happening in the BT scanning method will now happen in the background
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:[self getTopView:self.view] animated:YES];
});
});
/** Recursion to get the top most view in view layer. */
- (UIView *)getTopView:(UIView *)view
{
if ([view.superview class]) {
return [self getTopView:view.superview];
}
return view;
}
I'm request scanning bt in a popover, but I want to show HUD in a main View, so I write a block to retrieve the main view. Maybe that's where the problem occur?
Try this:
In your viewDidload or the method where you want to place it
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view];
[hud setDimBackground:YES];
[hud setOpacity:0.5f];
[hud show:YES];
[hud hide:YES afterDelay:5.0];
[self performSelector:#selector(startScanning) withObject:nil afterDelay:5.0];
And your method
- (void) startScanning {
self.btDevices = [Util scanBT];
}
OR I think you should try it running
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.001 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
// Insert task code here
self.btDevices = [Util scanBT];
});
Try it with completion block
[hud showAnimated:YES whileExecutingBlock:^{
self.btDevices = [Util scanBT];
} completionBlock:^{
//code for after completion
}];
OR you can also try this
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_queue_t myqueue = dispatch_queue_create("queue", NULL);
dispatch_async(myqueue, ^{
//Whatever is happening in the BT scanning method will now happen in the background
self.btDevices = [Util scanBT];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});

UIActivityIndicator not showing over UITableView when reloading new data

My basic problem is that I cannot seem to work out how to bring the Activity indicator to the top so I can show that a process is running while new data is populating the UITableView. To load new data I am using a Segmented Control. When segment 0 is pressed it will load the first xml feed and when segment 1 is pressed it will load a second xml feed. I have tried using dispatch_queue_t etc. it does load the information and repopulate the table, it just doesn't show the indicator.
This is the code i am currently using.
if (Seg.selectedSegmentIndex == 0) {
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:#selector(loading) userInfo:nil repeats:YES];
dispatch_queue_t loader = dispatch_queue_create("a", NULL);
dispatch_async(loader, ^{
xml = [[XmlParser alloc]init];
url = [NSURL URLWithString:kTodaysReport];
dict = [[NSMutableDictionary alloc]init];
[xml loadXML:url];
[tableView addSubview:ActInd];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(handleUpdate:) name:#"update" object:nil];
});
});
}
else if (Seg.selectedSegmentIndex ==1){
// tableView = [[UITableView alloc]init];
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:#selector(loading) userInfo:nil repeats:YES];
dispatch_queue_t loader = dispatch_queue_create("a", NULL);
dispatch_async(loader, ^{
xml = [[XmlParser alloc]init];
url = [NSURL URLWithString:kMonthsReport];
dict = [[NSMutableDictionary alloc]init];
[xml loadXML:url];
[tableView addSubview:ActInd];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(handleUpdate:) name:#"update" object:nil];
});
});
}
Thanks in advance.
EDIT:Relevant code
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t loader = dispatch_queue_create("a", NULL);
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:#selector(loading) userInfo:nil repeats:YES];
dispatch_async(loader, ^{
xml = [[XmlParser alloc]init];
url = [NSURL URLWithString:kTodaysReport];
dict = [[NSMutableDictionary alloc]init];
[xml loadXML:url];
[UiTableView addSubview:ActInd];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(handleUpdate:) name:#"update" object:nil];
[ActInd stopAnimating];
[UiTableView reloadData];
});
});
}
-(void)handleUpdate:(NSNotification*)notification{
mainArray = [xml parsedArray];
[UiTableView reloadData];
}
-(void)loading{
if (![xml val]) {
[ActInd startAnimating];
}else
{
[ActInd stopAnimating];
[UiTableView reloadData];
}
}
-(IBAction)segvalueChange:(id)sender{
if (Seg.selectedSegmentIndex == 0) {
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:#selector(loading) userInfo:nil repeats:YES];
dispatch_queue_t loader = dispatch_queue_create("a", NULL);
dispatch_async(loader, ^{
xml = [[XmlParser alloc]init];
url = [NSURL URLWithString:kTodaysReport];
dict = [[NSMutableDictionary alloc]init];
[xml loadXML:url];
[UiTableView addSubview:ActInd];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(handleUpdate:) name:#"update" object:nil];
});
});
}
else if (Seg.selectedSegmentIndex ==1){
// UiTableView = [[UITableView alloc]init];
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:#selector(loading) userInfo:nil repeats:YES];
dispatch_queue_t loader = dispatch_queue_create("a", NULL);
dispatch_async(loader, ^{
xml = [[XmlParser alloc]init];
url = [NSURL URLWithString:kMonthsReport];
dict = [[NSMutableDictionary alloc]init];
[xml loadXML:url];
[UiTableView addSubview:ActInd];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(handleUpdate:) name:#"update" object:nil];
});
});
}
else{
}
}
The problem is that you are interacting with the UI on a background thread. Move all UI related code (including that which interacts with the activity view) into the GCD block for the main thread.

problems with new thread

After trying many ways to call a function in new thread only the below code worked for me
[NSThread detacNewThreadSelector:#selector(temp:) toTarget:self withObject:self];
The below didn't work:
NSThread *updateThread1 = [[NSThread alloc] initWithTarget:self selector:#selector(temp:) object:self];
NSThread *updateThread1 = [[NSThread alloc] init];
[self performSelector:#selector(temp:) onThread:updateThread1 withObject:self waitUntilDone:YES];
Now when i try to call NSTimer or perform selector in timer: function it does not works Find below the code
int timeOutflag1 = 0;
-(void)connectCheckTimeOut
{
NSLog(#"timeout");
timeOutflag1 = 1;
}
-(void)temp:(id)selfptr
{
//[selfptr connectCheckTimeOut];
NSLog(#"temp");
//[NSTimer scheduledTimerWithTimeInterval:5 target:selfptr selector:#selector(connectCheckTimeOut) userInfo:nil repeats:NO];
[selfptr performSelector:#selector(connectCheckTimeOut) withObject:nil afterDelay:5];
}
- (IBAction)onUart:(id)sender {
protocolDemo1 *prtDemo = [[protocolDemo1 alloc] init];
//NSThread *updateThread1 = [[NSThread alloc] initWithTarget:self selector:#selector(temp:) object:self];
//[self performSelector:#selector(temp:) onThread:updateThread1 withObject:self waitUntilDone:YES];
// [updateThread1 start];
[self performSelector:#selector(temp:) withObject:self afterDelay:0];
while(1)
{
NSLog(#"Whieloop");
if(timeOutflag1)
{
timeOutflag1 = 0;
break;
}
if([prtDemo isConnected])
break;
}
}
If i use [self performSelector:#selector(connectCheckTimeOut) withObject:nil afterDelay:5];
in onUart function then it works properly i can see Timeout printf but inside temp it does not work.
NSTimer is run-loop based, so if you want to use one on a background thread that you're spawning and managing yourself, you will need to start a runloop on that thread. Read up on NSRunLoop. The short version might look something like:
- (void)timedMethod
{
NSLog(#"Timer fired!");
}
- (void)threadMain
{
NSRunLoop* rl = [NSRunLoop currentRunLoop];
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: #selector(timedMethod) userInfo:nil repeats:YES];
[rl run];
}
- (void)spawnThread
{
[NSThread detachNewThreadSelector: #selector(threadMain) toTarget:self withObject:nil];
}

Stop gyro updates

I cant work out to stop the Gyro updates in my Cocos2D game,
I have got the following code in my init: ` //gyro
motionManager = [[CMMotionManager alloc] init];
referenceAttitude = nil;
[motionManager startGyroUpdates];
timer = [NSTimer scheduledTimerWithTimeInterval:0.2
target:self
selector:#selector(doGyroUpdate)
userInfo:nil
repeats:YES];
Then in the gyro update itself I check when the progress is more than 100% if (progress > 100) {
[self pauseSchedulerAndActions];
[self stopGyroUpdates];
Then:
- (void)stopGyroUpdates{
NSLog(#"Stop gyro update");
}
but it keeps checking... So the code within the if statement keeps getting called.
I found the solution with the following code: `
-(void) callgyro:(int)gyroprocess {
NSLog(#"%i", gyroprocess);
if (gyroprocess < 100 ) {
motionManager = [[CMMotionManager alloc] init];
referenceAttitude = nil;
[motionManager startGyroUpdates];
timertwee = [NSTimer scheduledTimerWithTimeInterval:0.2
target:self
selector:#selector(doGyroUpdate)
userInfo:nil
repeats:YES];
}
else {
[motionManager stopGyroUpdates];
[timertwee invalidate];
NSLog(#"Gyro moet stoppen!");
}`
and within the gyro update itself:
if (progress > 100) {
[self callgyro:progress];
[self.timer invalidate];
self.timer = nil;
[Blikje setImage:[UIImage imageNamed:#"pop3.png"]];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setFloat:numhundreds forKey:#"score"];
progress = 0;
[self stopGyroUpdates];
}
Like rickster said, you need to call stopGyroUpdates on the CMMotionManager instance.so you need to create an instance variable or property in the interface implementation.
In your .m file where your declare the interface:
#interface ViewController ()
#property (strong, nonatomic) CMMotionManager *motionManager;
#end
Then initialize it as you require
motionManager = [[CMMotionManager alloc] init];
Then when you need to stop updates you call
[self.motionManager stopGyroUpdates]
You need to call stopGyroUpdates on your instance of CMMotionManager, not (or in addition to) on self. (This means that instance needs to persist beyond the scope if the method in which you call startGyroUpdates -- say, in a property or ivar -- if it doesn't already.)

Resources