I'm testing a part of my code using using XCTest that also adds NSOperations on the main queue.
It looks like this:
[NSOperationQueue mainQueue] addOperationAsBlock:^{
// some code happens here
}];
The code runs when running the app on a device or in the simulator but doesn't run at all when running the unit test (I can't get to the debug point on the first line of the block).
calling:
[NSOperationQueue mainQueue] waitUntilAllOperationsAreFinished];
doesn't help as well.
Any suggestions? I think i'm missing some code to initialise the queue.
* EDIT *
Thanks for your answers, I added my resulting code for completeness:
// add as many operations as you'd like to the mainQueue here
__block BOOL continueCondition = YES;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// this should be the last operation
continueCondition = NO;
}];
while (continueCondition) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} // continue your test here
This works because the mainQueue is guaranteed to be non-concurrent so the last operation that's added will be the last one executed - this way you don't even have to change your code to stop the test loop.
Same as IOS -NSRunLoop in XCTest: How Do I Get A Run Loop to Work in A Unit Test?
Also, aquarius / XCTestCase+MNAsynchronousTestCase.h is helpful for it.
Related
I am trying to write test, that stubs the requests with OHHTTPStubs and then it should load the UI. The stub part is working, but the problem is, that test and UI loading are both executed on the main thread, so this block of loading ViewController never gets executed. Thanks for the tips in advance. Have a great day.
dispatch_block_t mainBlock = ^{
WDSomeVC *viewController = [[WDSomeVC alloc] initWithData:data andStyle:self.style];
viewController.delegate = self;
[self switchRootController:viewController withCompletion:nil];
};
dispatch_async(dispatch_get_main_queue(), mainBlock);
I solved it using this code.
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeToWait]];
This question already has answers here:
How do I perform several methods in sequence?
(2 answers)
Closed 8 years ago.
In my application I have four methods like
- (void)Method1;
- (void)Method2;
- (void)Method3;
- (void)Method4;
I want to execute these methods one after another. I search for this one in some sources they are using "dispatch_Time" in some sources they are using "NSThread sleepForTimeInterval:" But in my application I don't want to execute those methods using time. I want to execute them if the previous method execution is completed. How can I do this?
Solution Overview
The right way of doing this is to enqueue the methods onto a serial queue.
A serial queue executes one task at a time and thus ensures that a task is only executed once all it's predecessors were executed.
There are several ways of accomplishing what you wish. I will describe 2 of them, one using Grand Central Dispatch and one using NSOperationQueue.
Grand Central Dispatch
Create a queue onto you will enqueue your task. A good practice would be to keep the queue as an instance variable so that you could access it from instance methods (unlike NSOperationQueue, by default custom dispatch queues are serial by default, i.e. they execute one task at a time):
dispatch_queue_t my_queue = dispatch_queue_create("com.suresh.methodsqueue", NULL);
self.methods_queue = my_queue;
Enqueue your methods onto the designated queue one after the other:
dispatch_async(self.methods_queue, ^{ [someObject method1] });
dispatch_async(self.methods_queue, ^{ [someObject method2] });
dispatch_async(self.methods_queue, ^{ [someObject method3] });
dispatch_async(self.methods_queue, ^{ [someObject method4] });
Further re GCD in Apple's developer guides.
Operation Queue
Initialize a queue:
NSOperationQueue* aQueue = [[NSOperationQueue alloc] init];
self.methods_queue = aQueue;
Make sure the queue is serial:
[self.methods_queue setMaxConcurrentOperationCount:1]
Enqueue the methods onto the queue, there's several ways of doing so, the following requires the least amount of code:
[self.methods_queue addOperationWithBlock:^{ [someObject method1] }];
[self.methods_queue addOperationWithBlock:^{ [someObject method2] }];
[self.methods_queue addOperationWithBlock:^{ [someObject method3] }];
[self.methods_queue addOperationWithBlock:^{ [someObject method4] }];
Further re Operation Queues in Apple's developer guides.
If the methods don't return until they've finished their work, just call them in order:
[self Method1];
[self Method2];
[self Method3];
[self Method4];
Otherwise, you need to use something like dispatch groups.
call first method somewhere like
[self (void)Method1];
and implement like
(void)Method1{
// logic here
[self (void)Method2];
}
(void)Method2{
// logic here
[self (void)Method3];
}
and so on ...............
Sorry, it's a bit wordy, but I wanted to make sure I was clear! ;-)
I have an iOS app that uses FFMPEG for streaming RTSP. I've multi-threaded FFMPEG using NSOperationQueue such that most its work, other than painting the image to the screen, of course, happens in background threads.
Works great! ...except for the fact that threads the NSOperationQueue creates never die!
I init the Queue in the class' init method with:
self->opQ = [[NSOperationQueue alloc] init];
[self->opQ setMaxConcurrentOperationCount:1];
I add methods to the Queue using blocks:
[self->opQ addOperationWithBlock:^{
[self haveConnectedSuccessfullyOperation];
}];
Or
[self->opQ addOperationWithBlock:^{
if (SOME_CONDITION) {
[self performSelectorOnMainThread:#selector(DO_SOME_CRAP) withObject:nil waitUntilDone:NO];
}
}];
Later, when I need to tear down the RTSP stream, in addition to telling FFMPEG to shut down, I call:
[self->opQ cancelAllOperations];
Which does indeed stop the threads from doing any work , but never actually destroys them. Below, you'll see a screen shot of threads that are doing nothing at all. This is what my threads look like after starting/stoping FFMPEG several times.
I seem to remember reading in Apple's documentation that NSOperations and the threads they are run on are destroyed once they are done executing, unless otherwise referenced. This doesn't appear to be the case.
Do I just need to destroy the NSOperationQueue, then re-init it when I need to start up FFMPEG again (I just realized I haven't tried this)? Anyone know how I need to kill these extra threads?
THANKS!
I solved it by creating NSBlockOperations so that I could monitor the isCancelled state, while also making the new NSBlockOperations' content more intelligent, such that I simplified the routine that would add the operations to the queue.
... Plus, I made an NSOperationQueue n00b mistake: I was adding operations to the queue on a looping basis, which fired up to 30 times per second (matching the video's frame rate). Now, however, the operation is added to the queue only once and the looping behavior is contained within the operation instead of having the loop add the operation to the queue.
Previously, I had something like this (pseudo code, since I don't have the project with me):
NSTimer *frameRateTimeout = [NSTimer scheduledTimerWithTimeInterval:1/DESIRED_FRAMES_PER_SECOND target:self selector:#selector(ADD_OPERATION_TO_QUEUE_METHOD:) userInfo:nil repeats:YES];
-(void)ADD_OPERATION_TO_QUEUE_METHOD:(NSTimer *)timer {
[opQ addOperation:displayFrame];
}
Which worked well, as the OS would correctly manage the queue, but it was not very efficient, and kept those threads alive forever.
Now, it's more like:
-(id)init {
self = [super init];
if (self) {
// alloc/init operation queue
...
// alloc/init 'displayFrame'
displayFrame = [NSBlockOperation blockOperationWithBlock:^{
while (SOME_CONDITION && ![displayFrame isCancelled]) {
if (playVideo) {
// DO STUFF
[NSThread sleepForTimeInterval:FRAME_RATE];
}
else { // teardown stream
// DO STUFF
break;
}
}
}];
}
return self;
}
- (void)Some_method_called_after_getting_video_ready_to_play {
[opQ addOperation:displayFrame];
}
Thanks, Jacob Relkin, for responding to my post.
If anyone needs further clarification, let me know, and I'll post better code once I have the project in my hands again.
I read many posts about NSRunLoop, like this, this, this. But can't figure out what NSRunLoop actually does
What I usually see is a worker thread
wthread = [[NSThread alloc] initWithTarget:self selector:#selector(threadProc) object:nil];
[wthread start];
with a NSRunLoop inside it
- (void)threadProc
{
NSAutoreleasePool* pool1 = [[NSAutoreleasePool alloc] init];
BOOL isStopped = NO;
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (!isStopped)
{
{
NSAutoreleasePool* pool2 = [[NSAutoreleasePool alloc] init];
[runloop runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
[pool2 release];
}
}
[pool1 release];
}
And the main thread passes some work to this wthread
[self performSelector:#selector(someWork:) onThread:wthread withObject:nil waitUntilDone:NO];
In term of passing work from the main thread to the worker thread, I see many people do this. Why need NSRunLoop here ? What does it do ?
I read that NSRunLoop is used to manage events, why is there nothing except calling runMode inside threadProc ?
The example you've shown is a Cocoa idiom for creating a thread that will continue to run after the method -threadProc exits. Why?
Because:
the NSRunLoop instance you've created has at least one input source ([NSMachPort port])
you've explicitly started the run loop with runMode:beforeDate
Without adding an input source and explicitly starting the run loop, the thread would terminate.
Parenthetically, although run loops are still vital for managing events and certain asynchronous tasks, I would not view NSThread as the default way of architecting most asynchronous work in a Cocoa app nowadays. GCD is a far cleaner way of encapsulating background work.
EDIT:
Submitting work to a serial queue in GCD:
#interface Foo : NSObject
#end
#implementation Foo {
dispatch_queue_t _someWorkerQueue;
}
- (id)init {
self = [super init];
if( !self ) return nil;
_someWorkerQueue = dispatch_queue_create("com.company.MyWorkerQueue", 0);
return self;
}
- (void)performJob {
dispatch_async(_someWorkerQueue, ^{
//do some work asynchronously here
});
dispatch_async(_someWorkerQueue, ^{
//more asynchronous work here
});
}
#end
Much goes on behind the scene. The reason for this is it provides a way for the thread to stop executing when there are no work items. If you ever used a real time OS, tasks need a place to give up the processor so others can run.
What is not well documented is that when you send performSelector:onThread:..., it's the run loop that queues the message and the wakes up to let the thread process it. If you add log messages to the while loop you can see this happen.
For the really curious there is sample code on github you con get to play around with run loops - add a comment and I'll list a few.
I need to perform an asynchronous function execution because it is blocking the main thread and hence the UI is not available.
After looking at the questions in stackoverflow, I know there are three ways to do asynchronous function.
An example:
[NSThread detachNewThreadSelector:#selector(showSpinner:) toTarget:self withObject:self.view];
// or
[self performSelectorInBackground:#selector(showSpinner:) withObject:self.view];
// or
NSInvocationOperation *invOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(showSpinner:) object:self.view];
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperation:invOperation];
// or
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self showSpinner:self.view];
});
});
My question is how does performSelectorInBackground and detachNewThreadSelector return back to main thread? how do you know that they are done?
A couple of thoughts:
You might want to check Migrating Away From Threads in the Concurrency Programming Guide, which makes a compelling argument for dispatch queues and operation queues, which are discussed earlier in that same guide.
Also, as you delve into various asynchronous operations, remember, do time consuming stuff in background queues/threads, but always dispatch UI stuff back to the main queue. I only mention that because your task, showSpinner sounds a lot like a UI task, which you would never want to do in a background queue/thread. If it has some "expensive" non-UI related tasks, then fine, do that in the background, but make sure the UI stuff gets dispatched back to the main queue.
There are, as an aside, other renditions of the operations queues, e.g., block operations:
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperationWithBlock:^{
// do some slow stuff in the background here
// ok, now do UI stuff in the main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self showSpinner:self.view];
}];
}];
This is roughly equivalent to the GCD (dispatch queue) rendition:
dispatch_queue_t dispatchQueue = dispatch_queue_create("com.ramshad.app", 0);
dispatch_async(dispatchQueue, ^{
// do some slow stuff in the background here
// ok, now do UI stuff in the main queue
dispatch_async(dispatch_get_main_queue(), ^{
[self showSpinner:self.view];
});
});
There are tons of subtle pros and cons between the operation queues and dispatch queues (which we should not get into here because it's been discussed hundreds of times elsewhere on Stack Overflow), but both let you do surprisingly rich asynchronous operations with less complexity than traditional thread programming.
If you decide to stick with threads versus operation and/or dispatch queues (which I wouldn't necessarily recommend), you might want to check out the Threading Programming Guide.
To identify performSelectorInBackground & detachNewThreadSelector end of execution,call a method at the end of the thread method on main thread.
Additionaly NSThread provides an propery as isFinished which returns a Boolean value that indicates whether the receiver has finished execution.
Example:
[self performSelectorOnMainThread:#selector(threadMethod)
withObject:nil
waitUntilDone:NO];
or
[NSThread detachNewThreadSelector:#selector(threadMethod)
toTarget:self
withObject:nil];
-(void)threadMethod{
//here your implementation code
//here call the end notification method.
[self performSelectorOnMainThread:#selector(ThreadExecutionDone)
withObject:nil
waitUntilDone:YES];
}
-(void)ThreadExecutionDone{
//end of the performSelectorInBackground or detachNewThreadSelector.
}