Call performSelector NSObject instance without using self - ios

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.

Related

Significance of Target in detaching a Thread in Swift

I have recently started getting acquainted with explicit Multithreading in swift. I am trying to understand the below method to dispatch a new thread for executing a selector. while I am able to use it successfully, what I don't understand is what is the significance of target in the signature of the method below? is that argument used to hold a monitor lock for thread safety like in java ? I tried referring to the documentation with not much help. I would really appreciate if someone can help me understand what's happening under the hood here.
(void)detachNewThreadSelector:(SEL)selector
toTarget:(id)target
withObject:(id)argument;
The documentation for aTarget says:
The object that will receive the message aSelector on the new thread.
This means that the selector will be called on the object that you pass as the target. It's no different than making any other method call. You call a method on a specific instance of a class. The target is the specific instance. The selector is the method that is called on that instance.
Think of detachNewThreadSelector:toTarget:withObject: as calling the given method of the given object, with the given argument (or ignore the argument if the method has zero parameters), but call the method on a newly created thread.
For example:
[NSThread detachNewThreadSelector:#selector(expensiveComputationWithObjects:)
target:someCalculatorObject
withObject:someVeryLargeArray]
The method thus provides a very convenient way of dispatching method calls on background threads (though it doesn't allow reusing an existing thread).
Another minor disadvantage is that the methods in discussions need to have at most one parameter, though this limitation can be circumvented by having the target method receive a structure (a dictionary or another class) that holds the actual arguments.

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

How does UIApplication take an event from the top of event queue

I read this in the official doc:
First, the singleton UIApplication object takes an event from the top of the queue and dispatches it for handling.
How does this happen??
Also in the doc there is this section:
An App Receives Touches in the Touch-Handling Methods:
During a multitouch sequence, an app sends these messages when there are new or changed touches for a given touch phase; it calls the
touchesBegan:withEvent: method when one or more fingers touch down on the screen.....
Can somebody explain this line"..an app sends these messages...". It is very ambigious as to which "messages" are being talked about in the doc!!!!
Doc link:https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html#//apple_ref/doc/uid/TP40009541-CH2-SW2
Also, how internally does the UIApplication send the events to keyWindow??
Does it use sendEvent or does it have code something like
[[self keyWindow] touchesBegan:touches
withEvent:event];
How does this happen??
A queue is a first-in-first-out data structure. You put things in one end, and you take them out on the other. Exactly what method gets called to do this depends on how the OS happens to implement the event queue, but in the end it really doesn't matter at all, and in fact it may be done differently in different versions of the operating system. The event queue is an implementation detail, so you shouldn't make any assumptions about how it works.
Can somebody explain this line"..an app sends these messages...".
It just means that the app calls certain methods on certain objects. In Objective-C, "sending a message" is another way of saying "calling a particular object's instance method." If you have:
[foo bar];
you are sending message bar to foo.

How to uniquily identify selectors?

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.

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

Resources