I am scheduling a method to be called with an object in the near future and the object is just a random NSString that is gone as soon as I schedule the selector.
So I may say something like:
[self performSelector:#selector(runMethod:) withObject:#"randomString" afterDelay:1.0f];
If I need to cancel this BEFORE it fires documentation says to use:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(runMethod:) object:***];
• *The only issue is I don't know what the "object" is, it was just a random string that doesn't exist anymore and has been released by ARC by now.
How can I cancel any scheduled methods with a specific selector (in my case runMethod:) but without knowing the "object"?
Is there any way to get a list of all scheduled functions in the NSRunLoop and just iterate through them with a for loop looking for ones with specific selector names?
If you need to cancel things you should organise a better way to schedule them such that you can actually check what it scheduled and the details associated with it. A potential solution would be a custom class with a set of parameters. Internally this class runs a timer which executes the action at the specified fire time. An array of instances of this class would be trivial to search and cancel arbitrary items from.
If, as I understand, the randomString is useless for you, then pass nil to both performSelector... and cancelPreviousPerform...
Like this:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(runMethod) object:nil];
[self performSelector:#selector(runMethod) withObject:nil afterDelay:1.0f];
Related
Note: This question doesn't have anything to do with the language used, i.e, Swift/ Objective-C
I can't seem to get my head around how such a problem can be solved.
How an asynchronous method which processes data continuously ,return back these processed values to a function?
Example Class structure of -> ClassName
A method named -(void)infoCallBack , this is the method you have to call to get returns continuously.
Inside -(void)infoCallBack -> an asynchronous method [self startRecording]is there, which does audio recording asynchronously using AudioQueues by using a callback method void AudioInputCallback(..param..).
Finally Inside void AudioInputCallback(..param..) -> a method -(void) processAudio is there, which continuously processes data and gives us an integer number.
How to call a method like [ClassName infoCallBack] in such a way that we keep getting all these processed integers continuously?
Edit : I have searched SO, and came across completion handler blocks : although completion handlers only return a value once after completionHandler() is called. Moreover, another problem in this method was how to pass around this completionHandler to multiple methods in the className Structure as shown.
I came across delegates, it said that when distinct values are being returned continuously and state of something changes, then we should call a delegate. But I was stuck at how I would return values after I call the function infoCallBack from ClassName, i.e, [ClassName infoCallBack] which continuously can feed the person calling this function with the values being processed.
Actually I don't completely understand your question but I will give you the answer in my understanding
In my opinion, you can use block to handle process update.
typedef void (^ProcessHandlerBlock) (NSInteger i);
- (void)infoCallBackWithProcessHandlerBlock:(ProcessHandlerBlock)block {
[self startRecordingWithProcessHandlerBlock:(ProcessHandlerBlock)block];
}
- (void)startRecordingWithProcessHandlerBlock:(ProcessHandlerBlock)block {
[self audioInputCallbackWithParam1:#"1" param2:#"2" processHandlerBlock:(ProcessHandlerBlock)block];
}
- (void)audioInputCallbackWithParam1:(id)param1 param2:(id)param2 processHandlerBlock:(ProcessHandlerBlock)block {
[self processAudioWithProcessHandlerBlock:(ProcessHandlerBlock)block];
}
- (void)processAudioWithProcessHandlerBlock:(ProcessHandlerBlock)block {
// Assume this is your process method callback
[NSTimer scheduledTimerWithTimeInterval:1.f repeats:YES block:^(NSTimer * _Nonnull timer) {
// After receive integer, pass it to block.
block(1);
}];
}
When you want to use them, call
[self infoCallBackWithProcessHandlerBlock:^(NSInteger i) {
NSLog(#"%l", i); // Do whatever you want here
}];
Inside the completion block of infoCallBackWithProcessHandlerBlock method, you can use result integer to do what you want.
I can't seem to get my head around how such a problem can be solved.
From the rest of what you've written I suspect you can get your head around this, you probably just need a rest. You've found the answer already without realising...
I have searched SO, and came across completion handler blocks : although completion handlers only return a value once after completionHandler() is called.
The phrase "completion handler block" might be written as "a block used as a completion handler" - a block is just a block, you can call it as many times as needed, store it in variables, etc. While a completion handler is typically only called once a, say, result reporter might be called multiple times.
Moreover, another problem in this method was how to pass around this completionHandler to multiple methods in the className Structure as shown.
A block is a kind of object and like other objects your class instance might need to use in many methods it doesn't need to be passed around but can be stored in an instance variable. For ease of use first declare your a shorthand for the block type, say:
typedef void (^SampleHandler)(int i);
then declare a suitable instance variable:
#implementation ClassName
{
SampleHandler mySampleHandler;
}
now use this like any other instance variable - initialise it (mySampleHandler = ...;), use it to call the block (mySampleHandler(42);`)
I came across delegates, it said that when distinct values are being returned continuously and state of something changes, then we should call a delegate. But I was stuck at how...
Blocks and delegates are often used for similar purposes, you can use either to solve your problem in essentially the same way - store the block/delegate reference in an instance variable, call the block/delegate method to return a value. You just have to decide which suits your use case best:
A block is a function. A block is created inline, usually at the call site, and can reference (and sometimes modify) variables in the environment where it is created. Like any function it is (usually) a single-operation thing - pass argument(s), produce a side-effect/return a result.
A delegate is an object. Like any other object it requires an interface/implementation. Like any object it can have multiple methods, so its a multi-operation thing.
In your case - returning a value - a block is probably the best option, but you need to decide.
HTH
If a second method is called from a method that is running on a background thread, is the second method automatically ran in that same thread or does it happen back on the main thread?
Note: I want my second method to be handled in the background, but since I update the UI inside it, would doing the following, be the right way to do it:
- (void)firstMethod {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Do stuff in background
...
//Call a second method (Assume it'll run in this background thread as well)
[self secondMethod];
});
}
//Second Method
- (void)secondMethod {
//Do heavy lifting here
...
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI here
...
});
}
Update
Oh I totally forgot to mention that this method is something that loads suggestions into the view (think keyboard suggestions). Since every key tap would be calling this method, I only want it to run when a user has finished typing. The way I'm approaching it is by setting a 0.2 delay between keys and if a new key tap fall within that 0.2 delay it cancels the previous method call, and initiates a new call (this way, assuming the use types the word, "the", it doesn't run suggestions for "t", "th", "the". Since the user is typing pretty quickly we can assume they don't want suggestions for anything until they have stopped typing (allowing the call to go through after a 0.2s delay), or if they type slow (where they probably are looking for suggestions along the way).
So when calling my secondMethod I do the following:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(secondMethod) object:nil];
[self performSelector:#selector(secondMethod) withObject:nil afterDelay:0.2];
The problem is it's not being called (I'm assuming this method defaults it to be performed in the main thread?)
Generally speaking, nothing is going to hop between threads without being pretty explicit about it. Certainly something as trivial as just calling a method isn't. Your code seems fine. Remember to not access mutable state from more than one queue at once (for example if the heavy lifting uses instance variables, make sure that -firstMethod doesn't get called twice in a row. It'd spawn off two async calls to -secondMethod then, and they'd step all over each others data. If that's a problem, create a serial dispatch queue instead of using a global one).
The following code effectively schedules a delayed selector call and also effectively cancels all pending scheduled calls.
[self performSelector:#selector(triggerUpdateForNSIndexPath:)
withObject:indexPath
afterDelay:triggerIn];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
Also the following code schedules a selector for withObject and cancels that particular scheduled selector withObject.
[self performSelector:#selector(triggerUpdateForNSIndexPath:)
withObject:indexPath
afterDelay:triggerIn];
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(triggerUpdateForNSIndexPath:)
object:indexPath];
My problem is that this is using self at the target and that means any other scheduled calls none related to my selector method triggerUpdateForNSIndexPath will be canceled by the [NSObject cancelPreviousPerformRequestsWithTarget:self];. I have tried changing self to be an NSObject instance but the selector was never triggered.
How can I control scheduled selectors without having to know the exact withObject? To me creating an NSObject instance to replace using self seemed to make sense but I guess I'm off...
Essentially my worry is that in the future in my code I will call performSelector with a delay for a particular selector and the call [NSObject cancelPreviousPerformRequestsWithTarget:self]; will cancel it when it isn't related. This will introduce a ugly bug I want to avoid.
In addition to cancelPreviousPerformRequestsWithTarget:, NSObject also has cancelPreviousPerformRequestsWithTarget:selector:object: method.
From NSObject documentation:
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument
Cancels perform requests previously registered with performSelector:withObject:afterDelay:.
Parameters
aTarget
The target for requests previously registered with the performSelector:withObject:afterDelay: instance method
aSelector
The selector for requests previously registered with the performSelector:withObject:afterDelay: instance method.
anArgument
The argument for requests previously registered with the performSelector:withObject:afterDelay: instance method. Argument equality is determined using isEqual:, so the value need not be the same object that was passed originally. Pass nil to match a request for nil that was originally passed as the argument.
Discussion
All perform requests are canceled that have the same target as aTarget, argument as anArgument, and selector as aSelector. This method removes perform requests only in the current run loop, not all run loops.
I'm new to iOS so understanding this area was a bit difficult. What I ended up doing was to simply keep a map of the scheduled NSTimers. I could then cancel each individual one or all.
I am performing [self performSelector:#selector(<selctor>) withObject:<object> afterDelay:30];
in loop say after every event or action.
if the action fails i want to cancel the perform selector associated with it.
i know about
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(<selector>)
object:nil]
but how to cancel a specific perform selector from say list of all perform selectors called in loop.
You can't. Just don't use perform selectors. Instead, use NSOperation instances added to a queue or add NSInvocation instances to a list and execute them when you are sure that all tests are passed.
Unless you get some sort of unique reference back (like an object pointer) then you can't uniquely identify each request.
An alternative approach would be to use an NSTimer object to invoke the selector, which you can store somewhere and cancel whenever you like via its invalidate method.
I am aware that blocks are one of the latest feature added in ios. But I am really finding a
tough time learning it .
I have seen people doing the following
typedef void(^CallBackBlk) (NSString *);
#property(copy,nonatomic)CallBackBlk block;
and in .m class
-(void)doSomething:(CallBackBlk )cb{
self.block=cb;
}
I never understood what is the use of assigning it to cb here. Can't I simply do the following
-(void)doSomthing{
block(#"my string");
}
I am really not getting the purpose of storing the block in instance variable. Can any help
me with an example. Any help is greatly appreciated
In your doSomething method, where does block come from?
Answer that, and you'll have your reason.
Ah -- the commentary makes the question clear. Snark served a purpose (snark and too lazy to type out a real answer on my iPhone at 7AM :).
An instance variable is just a slot to put things. Nothing is in that slot to start with.
In your case, you could implement:
-(void)doSomething:(CallBackBlk )cb{
cb();
}
However, typically, a callback is used when you do something asynchronously. For example, you might do:
[myObject doSomething:^{
NSLog(#"did something");
}];
And then:
-(void)doSomething:(CallBackBlk)cb {
dispatch_async(... global concurrent queue ..., ^{
... do some work ...
cb();
});
}
That is, doSomething: will return as soon as the dispatch_async() happens. The callback block is used to callback to let you know that asynchronous operation is done.
Of course, still no need for an instance variable. Take that class that does something a bit further; make it some kind of relatively complex, state transitioning, engine. Say, like your average internet downloader or compute heavy simulation engine. At that point, lumping all your background work into a single method would be overly complex and, thus, shoving the callback block(s) (there may likely be more than one; a progress updater, a completion block and/or an error block, for example) into instance variables allow the class's implementation to be subdivided along lines of functionality more cleanly.
What is the use of storing the block in an instance variable
Perhaps to be able to access it later?
You would do that if you want to invoke the block later, after the method that assigns it has already returned.
Consider for example an object that manages a download. You might want to have a block that gets invoked when the download completes (e.g. to update the UI), but you don't want the download method to have to wait until that happens (because it might take a long time).
maybe and example of use will help..
one use for storing it as a variable i have found is if you have multiple views that all access another view (for me it was a map on the next view) i used blocks that were setup by the previous view (set the default location for the map, initialise markers and so forth) then passed it through to the next view, where it would run it, setting up the map. it was handy having the block use the local variables of the previous view to access certain attributes. it wasnt the only way to do it, but i found it was a nice clean way of going about it.
and here is an example of what gets run in the viewDidLoad of the mapview
if(setupMap){
setupMap(mapView);
}
if(gpsUpdate){
gpsUpdate(mapView);
}
if(addMarker){
addMarker(mapView);
}
now if those blocks were assigned (the if statement check if they are nil), it would run them and do the appropriate setup for the map. not every view needed to do those, so they would only pass to the map view what needed to be done. this keeps the map view very general purpose, and code gets reused a lot. write once use lots!
To use the block, you call your doSomething: method:
CallBackBlk laterBlock = ^(NSString *someString) {
NSLog(#"This code is called by SomeClass at some future time with the string %#", someString);
};
SomeClass *instance = [[SomeClass alloc] init];
[instance doSomething:laterBlock];
As you code the implementation of your class, it will presumably reach some condition or finish an action, and then call the laterBlock:
if (someCondition == YES) {
self.block("Condition is true");
}