Very similar issue is already discussed here. The problem at hand and what I am trying to achieve is to call a function on a given object in the thread it is created at. Here is the complete case:
an instance of class A is created in a given NSThread (Thread A) (not the main one). The instance keeps its creating NSThread as a member variable.
an instance of class B has one of its member functions executing in another NSThread - Thread B, and wants to call a function of A in A's creation thread. Thus B's currently executing function issues the following call:
[_a performSelector: #(fun)
onThread: _a.creationThread
withObject: nil
waitUntilDone: NO];
If the creation thread of A's instance is not the main one, fun never gets called. If the creation thread is the main one it is always called. First I was thinking whether the thread that created A's instance has been destroyed and the pointer points to an invalid thread but actually calling any functions on the thread object (Thread A) produces valid results and no crashes. Also checking the object is valid according to this check. Any suggestions?
Update:
What I'm doing is creating a timer on a background thread:
_timer = [NSTimer timerWithTimeInterval:60.0 target:self selector:#selector(fun:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
This code is the one starting the timer. The timer is not started in any specific background thread. It just happens that the function that creates the timer could be called in any thread. Thus the timer should be invalidated in the exactly same one as the NSTimer documentation states: "you should always call the invalidate method from the same thread on which the timer was installed."
To run timer on background thread, you have two options.
Use dispatch timer source:
#property (nonatomic, strong) dispatch_source_t timer;
and you can then configure this timer to fire every two seconds:
- (void)startTimer {
dispatch_queue_t queue = dispatch_queue_create("com.domain.app.timer", 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.timer, ^{
// call whatever you want here
});
dispatch_resume(self.timer);
}
- (void)stopTimer {
dispatch_cancel(self.timer);
self.timer = nil;
}
Run NSTimer on background thread. To do this, you can do something like:
#property (atomic) BOOL shouldKeepRunning;
#property (nonatomic, strong) NSThread *timerThread;
#property (nonatomic, strong) NSTimer *timer;
And
- (void)startTimerThread {
self.timerThread = [[NSThread alloc] initWithTarget:self selector:#selector(startTimer:) object:nil];
[self.timerThread start];
}
- (void)stopTimerThread {
[self performSelector:#selector(stopTimer:) onThread:self.timerThread withObject:nil waitUntilDone:false];
}
- (void)startTimer:(id)__unused object {
#autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:YES];
[runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
self.shouldKeepRunning = YES;
while (self.shouldKeepRunning && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
;
self.timerThread = nil;
}
}
- (void)handleTimer:(NSTimer *)timer {
NSLog(#"tick");
}
- (void)stopTimer:(id)__unused object {
[self.timer invalidate];
self.timer = nil;
self.shouldKeepRunning = FALSE;
}
I'm not crazy about the shouldKeepRunning state variable, but if you look at the Apple documentation for the run method, they discourage the reliance upon adding sources/timers to run loops:
If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop. A simple example would be:
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
Personally, I'd recommend the dispatch timer approach.
I need to do a possibly long series of calls that must occur on the main thread (because otherwise UIKit will balk). By "long" I mean 10,000 operations lasting .1 second each on an iPad 3.
Obviously, It's probably not the best idea to just loop through all of them at once.
I don't know how to execute all these on the main thread while leaving enough breathing room to keep UIKit responsive and the watchdog asleep (ie. not get terminated for hogging the run loop).
Does anybody have an idea? I will be targeting iOS 5.
Specifically what I'm trying to do is cache UITextPositions, because a UITextView is apparently taking a non-cached, iterative approach at getting UITextPositions, which means it is very, very slow at doing positionFromPosition:textview.beginningOfDocument offset:600011, but much faster at getting positionFromPosition:aPositionAt600000 offset:11. In fact, in my test case, the former takes over 100 seconds (on the main thread!), while the latter is virtually instantaneous.
Why do you want to do it on the main thread? The typical answer is to do these operations on a background thread, and send UI updates back to the main thread. For example, you could use Grand Central Dispatch:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do my time consuming task and everytime it wants to update the UI,
// it should dispatch that back to the main queue, e.g.
for (NSInteger i = 0; i < 10000; i++)
{
// do my background work
// now update the UI
dispatch_async(dispatch_get_main_queue(), ^{
// update the UI accordingly
});
}
});
Update:
It sounds like you have to do this in the foreground, so perhaps using a NSTimer might be better. I'm not a big NSTimer guy, but it might look something like the following.
First, make sure you have a class instance variable for it:
NSTimer *_timer;
Next, you can initialize it with:
- (void)startTimer
{
_timer = [NSTimer timerWithTimeInterval:0.0 target:self selector:#selector(timerCallback:) userInfo:nil repeats:YES];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addTimer:_timer forMode:NSDefaultRunLoopMode];
}
This will then invoke the timerCallback, perhaps processing a single UITextPosition on each invocation:
- (void)timerCallback:(NSTimer*)theTimer
{
BOOL moreTextPositionsToCalculate = ...;
if (moreTextPositionsToCalculate)
{
// calculate the next UITextPosition
}
else
{
[self stopTimer];
}
}
and when you're done, you could stop your timer like so:
- (void)stopTimer
{
[_timer invalidate];
_timer = nil;
}
I want to run certain background tasks.
Scenario: I would like a button to activate a thread or timer, and then have the thread/timer to start repeating every second returning a NSRunInformationalAlertPanel to the user with data.
This is what I have for my timer:
-(void)workerThread:(NSTimer*) theTimer {
if(intNumberOfTicks > 0)
{
NSRunInformationalAlertPanel(#"The Serial", [NSString stringWithFormat:#"%d", intNumberOfTicks], #"OK", nil, nil);
//[txtTimeMinutes setStringValue:[NSString stringWithFormat:#"%d", intNumberOfTicks]];
intNumberOfTicks--;
}
else {
[timer invalidate];
}
}
And for starting the method...
intNumberOfTicks = 5;
timer = [[NSTimer scheduledTimerWithTimeInterval:1 target: self selector:#selector(workerThread:) userInfo:self repeats:true] retain];
// Or for threading...
///[NSThread detachNewThreadSelector:#selector(workerThread) toTarget:self withObject:nil];
Can anyone help me implement what I need, maybe providing the most basic examples for a NSThread or NSTimer. I have looked at the Apple Dev Refrences but no luck.
Using NSTimer will execute the selector in the same thread as the one which instantiated and invoked it.
If your task must be carried out in a background thread try calling performSelectorInBackground:withObject:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/performSelectorInBackground:withObject:
From that background thread you can use a scheduled timer the way that you described above.
I've been searching for and attempting to program for myself, an answer to this question.
I've got a secondary thread running inside my mainView controller which is then running a timer which counts down to 0.
Whilst this timer is running the secondary thread which initiated the timer should be paused/blocked whatever.
When the timer reaches 0 the secondary thread should continue.
I've Experimented with both NSCondition and NSConditionLock with no avail, so id ideally like solutions that solve my problem with code, or point me to a guide on how to solve this. Not ones that simply state "Use X".
- (void)bettingInit {
bettingThread = [[NSThread alloc] initWithTarget:self selector:#selector(betting) object:nil];
[bettingThread start];
}
- (void)betting {
NSLog(#"betting Started");
for (int x = 0; x < [dealerNormalise count]; x++){
NSNumber *currSeat = [dealerNormalise objectAtIndex:x];
int currSeatint = [currSeat intValue];
NSString *currPlayerAction = [self getSeatInfo:currSeatint objectName:#"PlayerAction"];
if (currPlayerAction != #"FOLD"){
if (currPlayerAction == #"NULL"){
[inactivitySeconds removeAllObjects];
NSNumber *inactivitySecondsNumber = [NSNumber numberWithInt:10];
runLoop = [NSRunLoop currentRunLoop];
betLooper = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(betLoop) userInfo:nil repeats:YES];
[runLoop addTimer:[betLooper retain] forMode:NSDefaultRunLoopMode];
[runLoop run];
// This Thread needs to pause here, and wait for some input from the other thread, then continue on through the for loop
NSLog(#"Test");
}
}
}
}
- (void)threadKiller {
[betLooper invalidate];
//The input telling the thread to continue can alternatively come from here
return;
}
- (void)betLoop {
NSLog(#"BetLoop Started");
NSNumber *currentSeconds = [inactivitySeconds objectAtIndex:0];
int currentSecondsint = [currentSeconds intValue];
int newSecondsint = currentSecondsint - 1;
NSNumber *newSeconds = [NSNumber numberWithInt:newSecondsint];
[inactivitySeconds replaceObjectAtIndex:0 withObject:newSeconds];
inacTimer.text = [NSString stringWithFormat:#"Time: %d",newSecondsint];
if (newSecondsint == 0){
[self performSelector:#selector(threadKiller) onThread:bettingThread withObject:nil waitUntilDone:NO];
// The input going to the thread to continue should ideally come from here, or within the threadKiller void above
}
}
You can't run a timer on a thread and sleep the thread at the same time. You may want to reconsider whether you need a thread at all.
There's a few things that need to be pointed out here. First, when you schedule your timer:
betLooper = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(betLoop:)
userInfo:nil
repeats:YES];
it's added to and retained by the current run loop by that method, so you don't need to do that manually. Just [myRunLoop run]. Your timer's selector argument is also invalid -- a timer's "target method" needs to look like this:
- (void)timerFireMethod:(NSTimer *)tim;
This also means that you don't need to retain the timer if all you want to do is invalidate it, since you will have a reference to it from inside that method.
Second, it's not clear what you mean by "this thread needs to sleep to wait for input". When you schedule that timer, the method (betLoop) is called on the same thread. If you were to sleep the thread, the timer would stop too.
You seem to be a little mixed up regarding methods/threads. The method betting is running on your thread. It is not itself a thread, and it's possible to call other methods from betting that will also be on that thread. If you want a method to wait until another method has completed, you simply call the second method inside the first:
- (void)doSomethingThenWaitForAnotherMethodBeforeDoingOtherStuff {
// Do stuff...
[self methodWhichINeedToWaitFor];
// Continue...
}
I think you just want to let betting return; the run loop will keep the thread running, and as I said, the other methods you call from methods on the thread are also on the thread. Then, when you've done the countdown, call another method to do whatever work needs to be done (you can also invalidate the timer inside betLoop:), and finalize the thread:
- (void)takeCareOfBusiness {
// Do the things you were going to do in `betting`
// Make sure the run loop stops; invalidating the timer doesn't guarantee this
CFRunLoopStop(CFRunLoopGetCurrent());
return; // Thread ends now because it's not doing anything.
}
Finally, since the timer's method is on the same thread, you don't need to use performSelector:onThread:...; just call the method normally.
You should take a look at the Threading Programming Guide.
Also, don't forget to release the bettingThread object that you created.
NSThread has a class method + (void)sleepForTimeInterval:(NSTimeInterval)ti. Have a look at this :).
NSThread Class Reference
I am testing some code that does asynchronous processing using Grand Central Dispatch. The testing code looks like this:
[object runSomeLongOperationAndDo:^{
STAssert…
}];
The tests have to wait for the operation to finish. My current solution looks like this:
__block BOOL finished = NO;
[object runSomeLongOperationAndDo:^{
STAssert…
finished = YES;
}];
while (!finished);
Which looks a bit crude, do you know a better way? I could expose the queue and then block by calling dispatch_sync:
[object runSomeLongOperationAndDo:^{
STAssert…
}];
dispatch_sync(object.queue, ^{});
…but that’s maybe exposing too much on the object.
Trying to use a dispatch_semaphore. It should look something like this:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[object runSomeLongOperationAndDo:^{
STAssert…
dispatch_semaphore_signal(sema);
}];
if (![NSThread isMainThread]) {
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
} else {
while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
}
This should behave correctly even if runSomeLongOperationAndDo: decides that the operation isn't actually long enough to merit threading and runs synchronously instead.
In addition to the semaphore technique covered exhaustively in other answers, we can now use XCTest in Xcode 6 to perform asynchronous tests via XCTestExpectation. This eliminates the need for semaphores when testing asynchronous code. For example:
- (void)testDataTask
{
XCTestExpectation *expectation = [self expectationWithDescription:#"asynchronous request"];
NSURL *url = [NSURL URLWithString:#"http://www.apple.com"];
NSURLSessionTask *task = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
XCTAssertNil(error, #"dataTaskWithURL error %#", error);
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *) response statusCode];
XCTAssertEqual(statusCode, 200, #"status code was not 200; was %d", statusCode);
}
XCTAssert(data, #"data nil");
// do additional tests on the contents of the `data` object here, if you want
// when all done, Fulfill the expectation
[expectation fulfill];
}];
[task resume];
[self waitForExpectationsWithTimeout:10.0 handler:nil];
}
For the sake of future readers, while the dispatch semaphore technique is a wonderful technique when absolutely needed, I must confess that I see too many new developers, unfamiliar with good asynchronous programming patterns, gravitate too quickly to semaphores as a general mechanism for making asynchronous routines behave synchronously. Worse I've seen many of them use this semaphore technique from the main queue (and we should never block the main queue in production apps).
I know this isn't the case here (when this question was posted, there wasn't a nice tool like XCTestExpectation; also, in these testing suites, we must ensure the test does not finish until the asynchronous call is done). This is one of those rare situations where the semaphore technique for blocking the main thread might be necessary.
So with my apologies to the author of this original question, for whom the semaphore technique is sound, I write this warning to all of those new developers who see this semaphore technique and consider applying it in their code as a general approach for dealing with asynchronous methods: Be forewarned that nine times out of ten, the semaphore technique is not the best approach when encounting asynchronous operations. Instead, familiarize yourself with completion block/closure patterns, as well as delegate-protocol patterns and notifications. These are often much better ways of dealing with asynchronous tasks, rather than using semaphores to make them behave synchronously. Usually there are good reasons that asynchronous tasks were designed to behave asynchronously, so use the right asynchronous pattern rather than trying to make them behave synchronously.
I’ve recently come to this issue again and wrote the following category on NSObject:
#implementation NSObject (Testing)
- (void) performSelector: (SEL) selector
withBlockingCallback: (dispatch_block_t) block
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self performSelector:selector withObject:^{
if (block) block();
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}
#end
This way I can easily turn asynchronous call with a callback into a synchronous one in tests:
[testedObject performSelector:#selector(longAsyncOpWithCallback:)
withBlockingCallback:^{
STAssert…
}];
Generally don't use any of these answers, they often won't scale (there's exceptions here and there, sure)
These approaches are incompatible with how GCD is intended to work and will end up either causing deadlocks and/or killing the battery by nonstop polling.
In other words, rearrange your code so that there is no synchronous waiting for a result, but instead deal with a result being notified of change of state (eg callbacks/delegate protocols, being available, going away, errors, etc.). (These can be refactored into blocks if you don't like callback hell.) Because this is how to expose real behavior to the rest of the app than hide it behind a false façade.
Instead, use NSNotificationCenter, define a custom delegate protocol with callbacks for your class. And if you don't like mucking with delegate callbacks all over, wrap them into a concrete proxy class that implements the custom protocol and saves the various block in properties. Probably also provide convenience constructors as well.
The initial work is slightly more but it will reduce the number of awful race-conditions and battery-murdering polling in the long-run.
(Don't ask for an example, because it's trivial and we had to invest the time to learn objective-c basics too.)
Here's a nifty trick that doesn't use a semaphore:
dispatch_queue_t serialQ = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQ, ^
{
[object doSomething];
});
dispatch_sync(serialQ, ^{ });
What you do is wait using dispatch_sync with an empty block to Synchronously wait on a serial dispatch queue until the A-Synchronous block has completed.
- (void)performAndWait:(void (^)(dispatch_semaphore_t semaphore))perform;
{
NSParameterAssert(perform);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
perform(semaphore);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}
Example usage:
[self performAndWait:^(dispatch_semaphore_t semaphore) {
[self someLongOperationWithSuccess:^{
dispatch_semaphore_signal(semaphore);
}];
}];
There’s also SenTestingKitAsync that lets you write code like this:
- (void)testAdditionAsync {
[Calculator add:2 to:2 block^(int result) {
STAssertEquals(result, 4, nil);
STSuccess();
}];
STFailAfter(2.0, #"Timeout");
}
(See objc.io article for details.) And since Xcode 6 there’s an AsynchronousTesting category on XCTest that lets you write code like this:
XCTestExpectation *somethingHappened = [self expectationWithDescription:#"something happened"];
[testedObject doSomethigAsyncWithCompletion:^(BOOL succeeded, NSError *error) {
[somethingHappened fulfill];
}];
[self waitForExpectationsWithTimeout:1 handler:NULL];
Here is an alternative from one of my tests:
__block BOOL success;
NSCondition *completed = NSCondition.new;
[completed lock];
STAssertNoThrow([self.client asyncSomethingWithCompletionHandler:^(id value) {
success = value != nil;
[completed lock];
[completed signal];
[completed unlock];
}], nil);
[completed waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
[completed unlock];
STAssertTrue(success, nil);
Swift 4:
Use synchronousRemoteObjectProxyWithErrorHandler instead of remoteObjectProxy when creating the remote object. No more need for a semaphore.
Below example will return the version received from the proxy. Without the synchronousRemoteObjectProxyWithErrorHandler it will crash (trying to access non accessible memory):
func getVersion(xpc: NSXPCConnection) -> String
{
var version = ""
if let helper = xpc.synchronousRemoteObjectProxyWithErrorHandler({ error in NSLog(error.localizedDescription) }) as? HelperProtocol
{
helper.getVersion(reply: {
installedVersion in
print("Helper: Installed Version => \(installedVersion)")
version = installedVersion
})
}
return version
}
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[object blockToExecute:^{
// ... your code to execute
dispatch_semaphore_signal(sema);
}];
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
This did it for me.
Sometimes, Timeout loops are also helpful. May you wait until you get some (may be BOOL) signal from async callback method, but what if no response ever, and you want to break out of that loop?
Here below is solution, mostly answered above, but with an addition of Timeout.
#define CONNECTION_TIMEOUT_SECONDS 10.0
#define CONNECTION_CHECK_INTERVAL 1
NSTimer * timer;
BOOL timeout;
CCSensorRead * sensorRead ;
- (void)testSensorReadConnection
{
[self startTimeoutTimer];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW)) {
/* Either you get some signal from async callback or timeout, whichever occurs first will break the loop */
if (sensorRead.isConnected || timeout)
dispatch_semaphore_signal(sema);
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:CONNECTION_CHECK_INTERVAL]];
};
[self stopTimeoutTimer];
if (timeout)
NSLog(#"No Sensor device found in %f seconds", CONNECTION_TIMEOUT_SECONDS);
}
-(void) startTimeoutTimer {
timeout = NO;
[timer invalidate];
timer = [NSTimer timerWithTimeInterval:CONNECTION_TIMEOUT_SECONDS target:self selector:#selector(connectionTimeout) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
-(void) stopTimeoutTimer {
[timer invalidate];
timer = nil;
}
-(void) connectionTimeout {
timeout = YES;
[self stopTimeoutTimer];
}
Very primitive solution to the problem:
void (^nextOperationAfterLongOperationBlock)(void) = ^{
};
[object runSomeLongOperationAndDo:^{
STAssert…
nextOperationAfterLongOperationBlock();
}];
I have to wait until a UIWebView is loaded before running my method, I was able to get this working by performing UIWebView ready checks on main thread using GCD in combination with semaphore methods mentioned in this thread. Final code looks like this:
-(void)myMethod {
if (![self isWebViewLoaded]) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block BOOL isWebViewLoaded = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (!isWebViewLoaded) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((0.0) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
isWebViewLoaded = [self isWebViewLoaded];
});
[NSThread sleepForTimeInterval:0.1];//check again if it's loaded every 0.1s
}
dispatch_sync(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore);
});
});
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
}
}
//Run rest of method here after web view is loaded
}