This code works, and postSpamListUpdatedNotification is called
- (void) postSpamListUpdatedNotification
{
[NIDPrivateUtils postNotification:kNIDSpamListsUpdated andError:nil];
}
[self performSelector:#selector(postSpamListUpdatedNotification) withObject:nil];
But if I change it to this, then postSpamListUpdateNotification is never called. Why?
[self performSelector:#selector(postSpamListUpdatedNotification) withObject:nil afterDelay:2.0];
You likely don't have a runloop on this thread. performSelector:withObject:afterDelay: requires a runloop, but performSelector: doesn't.
Related
There is block that discovering CBServices of some CBPeripheral (CoreBluetooth). But how can I cancel this block after some time has passed?
For example, if it has not discover CBServices after 10 seconds my block must be cancel.
You can use performSelector for delay action
[self performSelector:#selector(cancelAction:) withObject:self afterDelay:10];
-(void)cancelAction:(id)sender {
//What ever you want try here
}
Just call stopScan function of CBCentralManager.
[self performSelector:#selector(stopScan:) withObject:self afterDelay:10];
- (void)stopScan:(id)sender {
[self.centralManager stopScan];
}
I've been trying to fix an issue in our NSOperation subclass and I feel it may be related to our manual change notifications for KVO. All the sources I've checked seem to do the following when updating the NSOperation state:
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
In contrast, we have been doing it like this:
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
[self didChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
Can anybody tell me why the former seems to be the recommended way of doing this?
It also seems that Apple's KVO docs recommend the first approach as well. Unfortunately they don't explain why.(https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOCompliance.html#//apple_ref/doc/uid/20002178-SW3)/
The reason is that, conceptually, the operation is changing both states together. You want observers to be notified only after the internal state has been updated for both properties, so that, when handling the notification, the observers see consistent state.
If you do:
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
[self didChangeValueForKey:#"isExecuting"];
then observers will get the change notification for the isExecuting property during that didChange... call. If they check the operation properties in their handler, they could see that the operation is not executing (isExecuting returns NO) but also not finished (isFinished still returns NO). That doesn't make sense. The observers could do something odd as a result.
I've implemented NSOperation without following the pattern
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
instead by simply doing
self.executing = NO;
self.finished = YES;
.. without problems (never ending operations or the like) when using NSOperationQueues. It seems NSOperationQueue only listens for 'IsFinished' to determine if an NSOperation is truly finished. This other answer explains better.
NSOperation KVO isFinished
I need to create a routine that save automatically a file content in a constant time period, ie, a backgroung loop that perform the save instructions. I thinked in use a recursive call of performSelector like below:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self performSelector:#selector(saveMethod) withObject:nil afterDelay:kTimeConstant];
}
- (void)saveMethod{
//The save logic should to be here
[self performSelector:#selector(saveMethod) withObject:nil afterDelay:kTimeConstant];
}
It works, but when I get out of viewController, it still running, and it must to stop.
are there any better way to execute it? Thank you!
This is probably a better implementation:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Start timer and sets it to a property called saveTimer
self.saveTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(saveMethod:)
userInfo:nil
repeats:YES];
}
- (void)saveMethod:(NSTimer*)theTimer {
// The save logic should to be here
// No recursion
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Stop timer
[self.saveTimer invalidate];
}
This is running on the main thread so it is probably not the best implementation but it should work better than what you currently have.
There is a function NSRunLoop cancelPreviousPerformRequestsWithTarget:selector:object: which allows you to cancel the performSelector call. Call this when you unload the view controller
ie.
[NSRunLoop cancelPreviousPerformRequestsWithTarget:self selector:#selector(saveMethod) object:nil];
Is there a way in the normal obj-c or cocos2d to make a delay inside a if-else block?
Like
if ([self isValidTileCoord:cTileCoord] && ![self isWallAtTileCoord:cTileCoord])
{
[self addChild:circle0];
//wait two seconds
//perform another task
}
Just a simple lag to wait between two tasks, or stalling an action. Is there any simple way to do this?
There are many ways to do this. I would use GCD
if ([self isValidTileCoord:cTileCoord] && ![self isWallAtTileCoord:cTileCoord])
{
[self addChild:circle0];
//wait two seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
//perform another task;
});
}
You can use the performSelector: withObject: afterDelay: method to delay tasks:
if ([self isValidTileCoord:cTileCoord] && ![self isWallAtTileCoord:cTileCoord])
{
[self addChild:circle0];
//wait two seconds
[self performSelector:#selector(continueTask) withObject:nil afterDelay:2.0];
}
And in the selector method:
-(void)continueTask
{
//perform another task
}
Inside the cocos2d you can also run an Action on the node like,
[self runAction:[CCSequence actions:[CCDelayTime actionWithDuration:2.0],[CCCallFunc actionWithTarget:self selector:#selector(anothertaks)],nil]];
Perform selector will also work but the problem is that all the schedulers of cocos2D get paused when the app go to background but on the other side perform selector will still count the time, so some time it create task,animation,etc syncing problems.
Perform selector:
[self performSelector:#selector(continueTask) withObject:nil afterDelay:2.0];
i've an app that when start control updates and other things. If the app find some updates they will ask user if this updates have to be done. If user select YES i want that a spinner appear on main screen until update finish. But when i tap YES my alert view doesn't disappear and remain on screen until update is finished.
Is it possible to create a thread that run on the main thread and stop when update in finished?
Thanks
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex==1) {
[self showActivityViewer];
[self downloadControlAndUpdatePoi];
[self downloadControlAndUpdateItinerari];
[self downloadControlAndUpdateArtisti];
[self downloadControlAndUpdateEventi];
[self hideActivityViewer];
NSLog(#"AGGIORNA");
} else {
NSLog(#"NON AGGIORNARE");
return;
}
}
If the methods
[self downloadControlAndUpdatePoi];
[self downloadControlAndUpdateItinerari];
[self downloadControlAndUpdateArtisti];
[self downloadControlAndUpdateEventi];
are executed synchronously (that means that they return only after having processed completely), so:
[self hideActivityViewer];
is executed only at the very end.
A simple approach to this is scheduling the execution of your methods on the main thread:
[self performSelector:#selector(downloadControlAndUpdatePoi) withObject:nil afterDelay:0];
....
[self hideActivityViewer];
so that those methods are executed only after control has returned to the main loop and the UI has been updated.
Otherwise, you could use:
+ detachNewThreadSelector:toTarget:withObject:
from NSThread, to do more or less the same. In this case I would suggest creating a wrapper method for all of your dowloadAndUpdate... methods, but keep in mind that you can't update the UI from a secondary thread.
In both cases, you should take some care about synchronizing the download... operations with the rest of your workflow after removing the alert view.