I'm trying to call a method after some delay.
I know there is a solution for that:
[self performSelector:#selector(myMethod) withObject:nil afterDelay:delay];
I saw this question and Documentation
But my question is: How can I call a method that takes two parameters??
for instance:
- (void) MoveSomethigFrom:(id)from To:(id)to;
How would I call this method with delay, using performSelector:withObject:afterDelay:
Thanks
use dispatch_after:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//code to be executed on the main queue after delay
[self MoveSomethingFrom:from To:to];
});
EDIT 2015: For Swift, i recommend using this small helper method: dispatch_after - GCD in swift?
You can also implement method in NSObject's category using NSInvocation object (works in all versions of iOS). I guess it should be something like this:
#interface NSObject(DelayedPerform)
- (void)performSelector:(SEL)aSelector withObject:(id)argument0 withObject:(id)argument1 afterDelay:(NSTimeInterval)delay {
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:aSelector];
[invocation setArgument:&argument0 atIndex:2];
[invocation setArgument:&argument1 atIndex:3];
[invocation performSelector:#selector(invoke) withObject:nil afterDelay:delay];
}
#end
Other ideas:
1)
You could use NSInvocations:
+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)signature
(>> see Eldar Markov's answer)
Documentation:
https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html
2) You could use a helper method..
[self performSelector:#selector(helperMethod) withObject:nil afterDelay:delay];
- (void) helperMethod
{
// of course x1 and x2 have to be safed somewhere else
[object moveSomethigFrom: x1 to: x2];
}
3) You could use an array or a dictionary as parameter..
NSArray* array = [NSArray arrayWithObjects: x1, x2, nil];
[self performSelector:#selector(handleArray:) withObject:array afterDelay:delay];
- (void) handleArray: (NSArray*) array
{
[object moveSomethigFrom: [array objectAtIndex: 0] to: [array objectAtIndex: 1]];
}
Swift:
let delayInSeconds = 3.0;
let delay = delayInSeconds * Double(NSEC_PER_SEC)
let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay));
dispatch_after(popTime, dispatch_get_main_queue(), {
// DO SOMETHING AFTER 3 sec
});
Here is how you can trigger a block after a delay in Swift:
runThisAfterDelay(seconds: 5) { () -> () in
print("Prints this 5 seconds later in main queue")
//Or perform your selector here
}
/// EZSwiftExtensions
func runThisAfterDelay(seconds seconds: Double, after: () -> ()) {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue(), after)
}
The argument count does not really matter.
Its included as a standard function in my repo:
https://github.com/goktugyil/EZSwiftExtensions
These will all work, but are all much more complex than is needed.
Design the method to be called with an NSDictionary argument. Put the objects in it you need.
If you want the method to be accessible by other means as well, call instead a method that 'unwraps' the dictionary and calls the intended method with explicit parameters.
Related
have written a method for updating which will call itself again and again when we start that activity, as mentioned below. I wants to stop this method function once we get an output (Now, it keeps running even if we get output). Please let me know if there is any possibility to do it.
-(void)reloadMethod{
[self downloadProgressAudio];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self reloadMethod];
});
}
If you need to keep calling this method until you get an output, the you need to include that test, and only call the function again if you don't have any output.
if !outputDataExists {
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self reloadMethod];
});
}
You can use timer here
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(reloadMethod)
userInfo:nil
repeats:YES];
Once you are done with your repeated execution then do this
[timer invalidate];
timer = nil;
I have a simple iOS app which needs to delay a certain block of code before it can be run.
I am using dispatch_time method to achieve this and it works very nicely. Just one question though, dispatch_time takes in an integer, so the time that you can set are only allowed to be whole numbers such as 1.0 seconds or 3.0 seconds, etc....
I want to set it to delay by 0.5 seconds. How can I do this? Below is my code:
int64_t time_delay = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, time_delay * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
/*
my block of code....
*/
});
Don't use a int64_t but a CGFloat. It will work fine.
CGFloat time_delay = 0.5f;
dispatch_time() takes nanoseconds as second parameter. Remove NSEC_PER_SEC from second parameter and your delay will be in nanoseconds
double delay = 2.25;
// 2250000000 = 2.25 * 1000000000
dispatch_time_t offset_time = delay * NSEC_PER_SEC;
// current mach_absolute_time + offset
dispatch_time_t fire_time = dispatch_time(DISPATCH_TIME_NOW, offset_time);
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.25), dispatch_get_main_queue(), ^{
dispatch_after(fire_time, dispatch_get_main_queue(), ^{
NSLog(#"time %llu", t);
});
This question already has answers here:
Returning method object from inside block
(3 answers)
Closed 8 years ago.
Very new to Objective-C and having a rough time figuring out how to accomplish the following. I come from a javascript background, so I may not be approaching this in the correct way.
In my view controller I'm making a call to a class method getLuminosity. I want to collect some float's from the camera for 7 seconds, average them, and then return that average but have no clue how to do it. Here is my getLuminosity code:
- (CGFloat) getLuminosity {
...
[vidCam startCameraCapture];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[vidCam stopCameraCapture];
NSNumber *averageLumin = [_arrayOfLumins valueForKeyPath:#"#avg.self"];
return [averageLumin floatValue];
});
return floatFromDispatchBlock;
}
Thanks for any help!
Your method getLuminosity invokes an asynchronous method. This inevitable makes the calling method asynchronous as well!
So, the first step to a working approach is to realize that your method getLuminosity is asynchronous, and an asynchronous method SHOULD provide a means to signal the call-site that the underlying asynchronous task is complete:
We might use a "completion handler" to accomplish this, but keep in mind that this is not the only way to achieve this (see #b52 answer how to accomplish this with promises).
The completion handler - a Block to be more precise - needs to be defined (that is, implemented) by the call-site. When the call-site "starts" the asynchronous task (through invoking the asynchronous method) the completion handler gets passed through to the asynchronous task. The task's responsibility is to "call" the completion handler when it is finished.
Usually, the completion handler has parameters which will be used to transfer the result of the asynchronous task. For example, we could define a completion block as follows:
typedef void (^completion_t)(NSNumber* averageLumin, NSError* error);
Note: usually, the "type" of the completion handler will be defined by the underlying asynchronous task, respectively its requirements. The implementation of the block, though, will be provided by the call-site.
Your asynchronous method can then look like this:
- (void) luminosityWithCompletion:(completion_t)completion;
And can be implemented:
- (void) luminosityWithCompletion:(completion_t)completion
{
...
[vidCam startCameraCapture];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[vidCam stopCameraCapture];
NSNumber *averageLumin = [_arrayOfLumins valueForKeyPath:#"#avg.self"];
if (completion) {
completion(averageLumin, nil)
}
});
On the call-site, you use a "continuation" to do whatever is necessary when the result is eventually available:
- (void) foo
{
...
// Define the "continuation" with providing the completion block:
// Put *everything* that shall be executed *after* the result is
// available into the completion handler:
[self luminosityWithCompletion:^(NSNumber* result, NSError*error) {
if (error) {
// handle error
}
else {
// continue on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
// using "result" on the main thread
float lum = [result floatValue];
...
});
}
}];
}
dispatch_after is the same as setTimeout in javascript. You can't return the contents of either one.
You've got two options, either put the current thread to sleep for 7 seconds (the processor will use the CPU core for something else for 7 seconds, then come back to it):
- (CGFloat) getLuminosity {
...
[vidCam startCameraCapture];
[NSThread sleepForTimeInterval:7.0];
[vidCam stopCameraCapture];
NSNumber *averageLumin = [_arrayOfLumins valueForKeyPath:#"#avg.self"];
return [averageLumin floatValue];
}
Or provide a callback block:
- (void) getLuminosityWithCallback:(void (^)(CGFloat averageLumin))callback; {
...
[vidCam startCameraCapture];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[vidCam stopCameraCapture];
NSNumber *averageLumin = [_arrayOfLumins valueForKeyPath:#"#avg.self"];
callback(averageLumin.floatValue);
});
}
[obj getLuminosityWithCallback:^(CGFloat averageLumin) {
...
}];
There are basically two ways. The first one would be to implement the getLuminosity method in a blocking synchronize fashion, which is not a good idea. The second would be to use asynchronous patterns, e.g. using delegates, blocks or promises (shameless self-promotion, although there are much more implementations available).
Since you have a background in JS, I assume you are familiar with the term promise. The following snippet shows how you would accomplish such task using promises in Objective-C:
#import <OMPromises/OMPromises.h>
- (OMPromise *)getLuminosity {
OMDeferred *deferred = [OMDeferred deferred];
[vidCam startCameraCapture];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(7 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[vidCam stopCameraCapture];
NSNumber *averageLumin = [_arrayOfLumins valueForKeyPath:#"#avg.self"];
[deferred fulfil:averageLumin];
});
return deferred.promise;
}
And you use it this way:
[[self getLuminosity] fulfilled:^(NSNumber *lumin) {
NSLog(#"%#", lumin);
}];
I need a special version of dispatch_after. I need it to pass a parameter to the anonymous function, and the anonymous function checks if the parameter and a class variable match before executing.
I already have it working like this:
double delayInSeconds = 5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
int mod = self.invalidationCount;
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(mod == self.invalidationCount)
[self doAction:speed accelTime:accel];
});
However, this is dirty as is. I have to copy this chunk around everywhere I need it in my class.
Here is what I would like:
Something along the lines of:
double delayInSeconds = 5;
dispatch_after_safe(delayInSeconds,self, ^(void){
[self doAction:speed accelTime:accel];
});
or if possible, in message form so I don't have to pass self, but thats no issue...
Essentially, dispatch_after_safe should do something like:
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
int mod = parameterSelf.invalidationCount;
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(mod == parameterSelf.invalidationCount)
//execute the parameter block
});
}
Essentially, I need a wrapper for what I am currently doing, but I'm new to objective C and I'm not quite sure syntactically how to do something like this.
The whole point of doing this is, the user can press Back which should halt any queued future events.
Thanks
Do you mean something like this:
void dispatch_after_safe(NSTimeInterval delay, id pmself, dispatch_block_t block) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC);
int mod = pmself.invalidationCount;
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (mod == pmself.invalidationCount) {
block();
}
});
}
(I recommend replacing the type id with whatever class of object it is supposed to be used with.)
Is it possible to make this "clearer" or "better" ?
Any solution is welcome, even thought i got a right answer.
The problem is dispatch_after() with popTime==0 is still giving time to the main thread to make some UI changes. The following code is sometimes called from a background thread.
-(void)methodCalledFromSomeThread{
if (delayInSeconds) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Updating some UI like
});
}else{
dispatch_async(dispatch_get_main_queue(), ^{
// Updating some UI like
});
}
}
dispatch_async on the main queue will also queue the code to run on the next runloop, giving time for the system to update the UI. You can check like so:
void (^block)() = ^{
[self.tableView reloadData];
};
if (delayInSeconds) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), block);
}else if([NSThread isMainThread]) {
block();
}
else {
dispatch_async(dispatch_get_main_queue(), block);
}