How to uniquily identify selectors? - ios

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.

Related

How to return multiple results from Asynchronous method?

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

cancelPreviousPerformRequests with arbitrary object

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];

Call performSelector NSObject instance without using self

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.

How to update UI before nsoperation will start and or end

2 part question but related so will keep in the same thread:
I'm adding NSOperations to a NSOperationQueue. I need to know when the operation will start and when it ends in order to update the UI.
I thought about adding a "start handler" block to run in the nsoperation as well as a "completion handler" in the NSOperation
Something like
-(id)initOperationWithStartBlock:(StartBlock)startblock completionBlock:(CompletionBlock)completionBlock
but believe that there is a better way to get this from the queue itself.
How can this be done?
I would also like to know the index of the job sent by the NSOperationQueue.
I've tried doing
[[self.myQueue operations] indexForObject:operation]
but the index is always the zeroth index - because the completed jobs were removed from the nsoperationqueue array before I could check the jobs index.
Any way to preserve them?
You need to use Key-Value-Observing pattern in IOS. So for this you need to setup observers in your controller to look for changes to isFinished and isExecuting to catch start and finish hooks.
It depends if you want to perform something from within your object upon starting or elsewhere in your code. From what you are saying (you want to update the UI), this sounds like you want to act outside of your object, but I don't know your program. You have two options:
1) If you want to act in your object upon starting the operation from within the same object, use key-value observation and observe isExecuting with self as the observer and the observed. Don't forget that you will get called whether it goes from NO to YES (starting) or YES to NO (done).
2) If you want to perform an action outside of the object, I would rather recommend to use the very general NSNotification with NSNotificationCenter and within your main, post a notification such as #"willStart" and #"didComplete". In any other object, register as an observer for your notifications.
Either way, don't forget that notifications are sent in the current threads but the UI must be updated on the main thread. You don't know on what thread observe:keyPath: is called. You may need to call performSelectorOnMainThread to update the UI or you can even use the convenient and useful nsoperationqueue mainqueue with a addOperationWithBlock with your UI code. If you use the NotificationCenter, then you can simply yourself post on the main thread with nsobject performSelectorOnMainThread

Is ok to release my object after I call performSelectorOnMainThread

Memory management is being done manually, ARC is not used in this project..
The message object is created using alloc init and the code below is being called on background thread.
I pass a message object before the following call:
[self performSelectorOnMainThread:#selector(serverConnectionResult:) withObject: message waitUntilDone:NO];
After the call I want to do:
[message release];
I am confused whether I should do this, because I am concerned whether the message object will be always valid when serverConnectionResult is called? Is the method call performSelectorOnMainThread retaining the message object itself? What's the rule to know that the called method retains my passed object?
It is safe to do this. -performSelectorOnMainThread:withObject:waitUntilDone: will retain both the target of the message and the object. Similarly -performSelector:withObject:afterDelay: will also retain the target and the object.
You could also use Grand Central Dispatch and use dispatch_async on the main thread and pass in a block that calls your method and afterwards releases the message.

Resources