I'm trying to do some stress testing on my app with a unit test, and I'm running into some problems. Below is my code:
//Stress test api and core data
__block BOOL done = NO;
for (int i = 0; i < 100 ; i++) {
DLog(#"in here");
[viewController createList:testList
success:^(Lists *list) {
DLog(#"in success block : %d", i);
STAssertNotNil(list, #"list is not nil");
done = (i == 99);
}
failure:^(NSError *error) {
DLog(#"in fail block");
}
];
}
#autoreleasepool {
while (!done) {
// This executes another run loop.
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
The problem is, after several iterations, I get a bad access error on the line
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
I've already put the while loop in the #autorelease because of this post. I'm using ARC on the project, so I don't know if that's contributing to the problem..? I need to use the NSRunLoop to force the unit test to wait for the block to complete.
Has anyone run into this problem?
In a similar situation, I did it differently and it worked fine. What I did was in the main test, used dispatch_group_async to the normal queue, and then in the main test waited for the group to finish. That works just fine and you don't mess with the unit test's runloop.
if for some reason the above is not acceptable, then try not using any autorelease pool to see if that works If it does, then move it into the while look. With ARC the need for an autorelease pool is greatly diminished. You can observe memory usage using Instruements too.
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]];
I am using the dropbox API with DBRESTClient and DBRestClientDelegate in Dropbox-iOS-SDK
My issue is that I need these to run on a background thread.
When I call the [restClient loadMetadata] I do not get a response to - restClient:loadedMetadata: unless I begin call from the main thread.
Is there a simple workaround/library that I can use which will allow a delegate response on a thread ? I have tried Dropblocks which uses blocks but no luck.
I noticed "Make sure you call DBRestClient methods from the main thread or a thread that has a run loop. Otherwise the delegate methods won't be called." on the Dropbox Page
https://www.dropbox.com/developers-v1/core/start/ios
I used a runloop and it functions on a thread now
This is using a completion block Similarly you could use the delegate to set the flag to NO
self.inQuery = YES;
[self loadMetaDataWithPath:rootFolder mediaType:#(mediaType) completion:^(BOOL complete) {
self.inQuery = NO;
}];
#autoreleasepool {
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
DDLogError(#"currentRunLoop %#",[NSDate date]);
}while(self.inQuery);
}
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.
We have a delegate method which will be called for approximately 20 times in a second. In the delegate method we are updating our UILabel which represents the counter like below mentioned code:
- (void) counterUpdated:(NSString *) value
{
lblCounter.text = [NSString stringWithString:value];
// [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
// [lblCounter setNeedDisplay];
}
I read similar problems in stack overflow and I implemented the solutions over there and I checked with keeping [lblCounter setNeedDisplay]; method and [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]]; after updating lblCounter, but it is not working well as expected.
Any pointers?
Try using Grand Central Dispatch in order to run this in the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
lblCounter.text = value;
});
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.