I am trying to setup an NSInovcation system to launch selectors into background threads using performSelectorInBackground: - So far everything is successful when running the system on instance methods (-), but I also want to support class methods (+). I have adjusted my code to provide an invokeInBackgroundThread for both types of class and everything worked except for one problem. When the class methods are invoked I get my console flooded with "autoreleased with no pool in place" messages. No idea what is causing it. The code which is based off the DDFoundation open source project is shown below.
#implementation NSObject (DDExtensions)
...
+ (id)invokeInBackgroundThread
{
DDInvocationGrabber *grabber = [DDInvocationGrabber invocationGrabber];
[grabber setInvocationThreadType:INVOCATION_BACKGROUND_THREAD];
return [grabber prepareWithInvocationTarget:self];
}
- (id)invokeInBackgroundThread
{
DDInvocationGrabber *grabber = [DDInvocationGrabber invocationGrabber];
[grabber setInvocationThreadType:INVOCATION_BACKGROUND_THREAD];
return [grabber prepareWithInvocationTarget:self];
}
...
...
- (void)forwardInvocation:(NSInvocation *)ioInvocation
{
[ioInvocation setTarget:[self target]];
[self setInvocation:ioInvocation];
if (_waitUntilDone == NO) {
[_invocation retainArguments];
}
if (_threadType == INVOCATION_MAIN_THREAD)
{
[_invocation performSelectorOnMainThread:#selector(invoke)
withObject:nil
waitUntilDone:_waitUntilDone];
} else {
[_invocation performSelectorInBackground:#selector(invoke)
withObject:nil];
}
}
...
+(void)doSomething;
[[className invokeOnBackgroundThread] doSomething];
Main thread has autorelease pool by default, if you start extra thread - it's your job to create the pool. Actually, nothing complicated here, just
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// Work...
[pool release];
Also, if you have a lot of threads, I'd suggest you to take a look at NSOperation instead of running threads with [performSelectorInBackground]. NSOperation (with wrapping queue) is more flexible solution for such tasks.
Related
Following TDD I'm developing an iPad app that downloads some info from the internet and displays it on a list, allowing the user to filter that list using a search bar.
I want to test that, as the user types in the search bar, the internal variable with the filter text is updated, the filtered list of items is updated, and finally the table view receives a "reloadData" message.
These are my tests:
- (void)testSutChangesFilterTextWhenSearchBarTextChanges
{
// given
sut.filterText = #"previous text";
// when
[sut searchBar:nil textDidChange:#"new text"];
// then
assertThat(sut.filterText, is(equalTo(#"new text")));
}
- (void)testSutReloadsTableViewDataAfterChangeFilterTextFromSearchBar
{
// given
sut.tableView = mock([UITableView class]);
// when
[sut searchBar:nil textDidChange:#"new text"];
// then
[verify(sut.tableView) reloadData];
}
NOTE: Changing the "filterText" property triggers right now the actual filtering process, which has been tested in other tests.
This works OK as my searchBar delegate code was written as follows:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
self.filterText = searchText;
[self.tableView reloadData];
}
The problem is that filtering this data is becoming a heavy process that right now is being done on the main thread, so during that time the UI is blocked.
Therefore, I thought of doing something like this:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *filteredData = [self filteredDataWithText:searchText];
dispatch_async(dispatch_get_main_queue(), ^{
self.filteredData = filteredData;
[self.tableView reloadData];
});
});
}
So that the filtering process occurs in a different thread and when it has finished, the table is asked to reload its data.
The question is... how do I test these things inside dispatch_async calls?
Is there any elegant way of doing that other than time-based solutions? (like waiting for some time and expect that those tasks have finished, not very deterministic)
Or maybe I should put my code on a different way to make it more testable?
In case you need to know, I'm using OCMockito and OCHamcrest by Jon Reid.
Thanks in advance!!
There are two basic approaches. Either
Make things synchronous only while testing. Or,
Keep things asynchronous, but write an acceptance test that does resynchronizing.
To make things synchronous for testing only, extract the code that actually does work into their own methods. You already have -filteredDataWithText:. Here's another extraction:
- (void)updateTableWithFilteredData:(NSArray *)filteredData
{
self.filteredData = filteredData;
[self.tableView reloadData];
}
The real method that takes care of all the threading now looks like this:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *filteredData = [self filteredDataWithText:searchText];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateTableWithFilteredData:filteredData];
});
});
}
Notice that underneath all that threading fanciness, it really just calls two methods. So now to pretend that all that threading was done, have your tests just invoke those two methods in order:
NSArray *filteredData = [self filteredDataWithText:searchText];
[self updateTableWithFilteredData:filteredData];
This does mean that -searchBar:textDidChange: won't be covered by unit tests. A single manual test can confirm that it's dispatching the right things.
If you really want an automated test on the delegate method, write an acceptance test that has its own run loop. See Pattern for unit testing async queue that calls main queue on completion. (But keep acceptance tests in a separate test target. They're too slow to include with unit tests.)
Albite Jons options are very good options most of the time, sometime it creates less cluttered code when doing the following. For example if your API has a lot small methods that are synchronised using a dispatch queue.
Have a function like this (it could be a method of your class as well).
void dispatch(dispatch_queue_t queue, void (^block)())
{
if(queue)
{
dispatch_async(queue, block);
}
else
{
block();
}
}
Then use this function to call the blocks in your API methods
- (void)anAPIMethod
{
dispatch(dispQueue, ^
{
// dispatched code here
});
}
You would usually initialise the queue in your init method.
#implementation MyAPI
{
dispatch_queue_t dispQueue;
}
- (instancetype)init
{
self = [super init];
if (self)
{
dispQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
Then have a private method like this, to set this queue to nil. It is not part of your interface, the API consumer will never see this.
- (void) disableGCD
{
dispQueue = nil;
}
In your test target you create a category to expose the GCD disabling method:
#interface TTBLocationBasedTrackStore (Testing)
- (void) disableGCD;
#end
You call this in your test setup and your blocks will be called directly.
The advantage in my eyes is debugging. When a test case involves a runloop so that blocks are actually called, the problem is that there has to be a timeout involved. This timeout is usually quite short because you don't want to have tests that last long if the they run into the timeout. But having a short timeout means your test runs into the timeout when debugging.
I need an extra thread in background to listen to requests from socket.
The code is put into a singleton class; it will be called in main.m before NSApplicationMain() like this:
[[SKSocketThread getSingleton] runThread];
And runThread is defined as follow:
- (void) runThread {
[NSThread detachNewThreadSelector:#selector(socketThreadMainLoop:)
toTarget:self
withObject:[self quitLock]];
}
- (void) socketThreadMainLoop:(id)param {
NSLock *lock = (NSLock *)param;
while (![lock tryLock]) {
NSLog(#"Yay! We are in socketThreadMainLoop now!");
[NSThread sleepForTimeInterval:2];
}
NSLog(#"Terminating the socket thread...");
[lock unlock]; // is it really necessary?
}
It compiled successfully with no warning, but will throw an error in runtime:
autoreleased with no pool in place.
I did some googling, tried to pack code in runThread and socketThreadMainLoop with #autoreleasepool, but the error is still there. Finally I wrapped call to runThread with it in main.m, and that worked!
I don't know why it only works this way...
You should wrap your code with #autoreleasepool block.
...
- (void) socketThreadMainLoop:(id)param {
#autoreleasepool
{
NSLock *lock = (NSLock *)param;
while (![lock tryLock]) {
NSLog(#"Yay! We are in socketThreadMainLoop now!");
[NSThread sleepForTimeInterval:2];
}
NSLog(#"Terminating the socket thread...");
[lock unlock]; // is it really necessary?
}
}
Read more:
NSAutoreleasePool Class Reference
Set a breakpoint on objc_autoreleaseNoPool and post the backtrace. You need an #autoreleasepool{...} in all threads that don't us run loops, including the main thread (in your main.m, if you aren't calling into NSApplicationMain()).
Some additional feedback; that you named the method getSingleton indicates that you are new to iOS development (don't name methods get* anything). That you are using sleep in a while loop indicates that you are a bit new to the whole networking thing, too.
Also, spinning up a thread prior to the call into NSApplicationMain() is totally the wrong thing to do; you should be doing the networking goop as a normal part of application startup... see below.
You really really really don't want to do networking using a handrolled while() loop with sleep. Polling is an awful pattern on mobile devices; it is battery hungry and that sleep is just going to make things unresponsive.
Use a proper run loop and/or dispatch sources and/or CFStream APIs and/or NSFileHandles.
In the initialization method of a class I am declaring the thread as such:
NSThread* myThread = [[[NSThread alloc] initWithTarget:self selector:#selector(m_run_thread) object:nil] autorelease];
[myThread start];
I also have a boolean value which is set to NO. Later on in the code I set the boolean value to YES.
bool_run_progress_thread = YES;
The contents of the method m_run_thread is as follows:
-(void) m_run_thread
{
if (bool_run_progress_thread)
{
//do processing here
}
bool_run_progress_thread = NO;
}
The problem is that the method m_run_thread is never being accessed. What am I doing wrong?
P.S. I have also tried to set up the Thread using the following (and older)method:
[NSThread detachNewThreadSelector:#selector(m_run_thread)
toTarget:self
withObject:nil];
... but to no avail as well.
"...and I am only getting it to show once" Yes, that's exactly how it should be. After being started, a thread runs once from its start to its end (ignoring errors here for the moment), and having reached the end, the thread is essentially dead and gone.
If you want the thread to repeat its execution, you have to prepare for that yourself:
- (void) m_run_thread
{
for (;;)
{
if (bool_run_progress_thread)
{
//do processing here
bool_run_progress_thread = NO;
}
}
}
But there is still a lot wrong with this code: essentially, when run, the code forms a busy waiting loop. Assuming, that bool_run_progress_thread is only ever true for short periods of time, the background thread should be sleeping most of the time. Insead, if you try the code as its stands, it will instead consume CPU time (and lots of it).
A better approach to this would involve condition variables:
#class Whatsoever
{
NSCondition* cvar;
BOOL doProgress;
...
}
...
#end
and
- (void) m_run_thread
{
for (;;)
{
[cvar lock];
while (!doProgress)
{
[cvar wait];
}
doProgress = NO;
[cvar unlock];
... do work here ...
}
}
and in order to trigger the execution, you'd do:
- (void) startProgress
{
[cvar lock];
doProgress = YES;
[cvar signal];
[cvar unlock];
}
Doing things this way also takes care of another subtle problem: the visibility of the changes made to the global flag (your bool_run_progress_thread, my doProgess). Depending on the processor and its memory order, changes made without special protection might or might not become (ever) visible to other threads. This problem is taken care of by the NSCondition, too.
So, I am using [NSThread detachNewThreadSelector] to spawn a new thread and I am getting "autoreleased with no pool in place " errors in the console. I know this can happen if you fail to create an auto release pool, but the thing is, I am creating one. I use similar code in other parts of the same app and do NOT get these errors.
Here is the relevant code:
- (void) startThread:(NSString*)strURL
{
// start new thread to load image
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSThread detachNewThreadSelector:#selector(loadImageFromURL:) toTarget:self withObject:strURL];
[pool release];
}
- (void) loadImageFromURL:(NSString*)strURL
{
NSNumber* nn = [NSNumber numberWithInt:self.tag];
NSLog(#"loadURL: Tag number == %i", [nn intValue]);
// other code here actually does the work
}
Now, there was more code in loadImageFromURL which actually does the work (of loading an image from a remote server) - but the problem manifests itself without that code, so I've removed it (just so you don't think I have a pointless thread which does nothing!). I left in just one line of code which demonstrates the problem - it creates an autoreleased NSNumber object.
When this code runs, it reports this to the console:
__NSAutoreleaseNoPool(): Object 0x535c0e0 of class NSCFNumber autoreleased with no pool in place - just leaking
Of course, the real code creates many other AR objects and all of them get reported as well.
Would be grateful for any tips or pointers which might help!
Thanks!
When you create a new thread, you need to also create a new autorelease pool for it. In your case, that looks as simple as adding:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
at the beginning of loadImageFromURL: and
[pool drain];
at the end.
You probably don't need or want the pool you're creating in startThread:. Check out the Threading Programming Guide, particularly the "Writing Your Thread Entry Routine" section.
On your code, - (void) startThread:(NSString*)strURL is running in the main thread, while - (void) loadImageFromURL:(NSString*)strURL is running on the background thread you are detaching.
The main thread already has a NSAutoreleasePool, so the one you are creating in startThread: is probably unneeded. However, the background thread will not create a NSAutoreleasePool, so you'd need to create it yourself.
In your code, that would look like:
- (void) startThread:(NSString*)strURL
{
// start new thread to load image
[NSThread detachNewThreadSelector:#selector(loadImageFromURL:) toTarget:self withObject:strURL];
}
- (void) loadImageFromURL:(NSString*)strURL
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSNumber* nn = [NSNumber numberWithInt:self.tag];
NSLog(#"loadURL: Tag number == %i", [nn intValue]);
// other code here actually does the work
[pool drain];
}
Also, as #Carl Norum suggested, you should use drain instead of release when you are done using the autorelelase pool.
Solution for a similar problem but using ARC.
If using ARC, you could get an error "'NSAutoreleasePool' is unavailable: not available in automatic reference counting mode".
Use:
- (void) startThread:(NSString*)strURL
{
// start new thread to load image
[NSThread detachNewThreadSelector:#selector(loadImageFromURL:) toTarget:self withObject:strURL];
}
- (void) loadImageFromURL:(NSString*)strURL
{
#autoreleasepool {
NSNumber* nn = [NSNumber numberWithInt:self.tag];
NSLog(#"loadURL: Tag number == %i", [nn intValue]);
// other code here actually does the work
}
}
Using OCUnit, is there a way to test delegate protocols?
I'm trying this, which doesn't work.
-(void) testSomeObjDelegate {
SomeObj obj = [[SomeObj alloc] initWithDelegate:self];
[obj executeMethod];
}
-(void) someObjDelegateMethod {
//test something here
}
I'm going to try calling the obj method on a different thread and have the test sleep until the delegate is called. It just seems like there should be an easier way to test this.
Testing a delegate is trivial. Just set an ivar in the test in your callback method, and check it after what should be triggering the delegate callback.
For example, if I have a class Something that uses a delegate of protocol SomethingDelegate and sends that delegate -something:delegateInvoked: in response to some message, I can test it lik ethis:
#interface TestSomeBehavior : SenTestCase <SomethingDelegate>
{
Something *_object;
BOOL _callbackInvoked;
}
#end
#implementation TestSomeBehavior
- (void)setUp {
[super setUp];
_object = [[Something alloc] init];
_object.delegate = self;
}
- (void)tearDown {
_object.delegate = nil;
[_object release];
[super tearDown];
}
- (void)testSomeBehaviorCallingBack {
[_object doSomethingThatShouldCallBack];
STAssertTrue(_callbackInvoked,
#"Delegate should send -something:delegateInvoked:");
}
- (void)something:(Something *)something delegateInvoked:(BOOL)invoked {
_callbackInvoked = YES;
}
#end
I think you already understand this, however, from the way you've phrased your question. (I'm mostly posting this for other readers.) I think you're actually asking a more subtle question: How do I test something that may occur later such as something that spins the runloop. My cue is your mention of sleeping and threading.
First off, you should not just arbitrarily invoke a method on another thread. You should only do so if it's documented to be safe to use in that way. The reason is that you don't know what the internals of the class do. For example, it might schedule events on the run loop, in which case running the method on a different thread will make them happen on a different run loop. This would then screw up the class's internal state.
If you do need to test something that may take a little time to happen, you can do this just by running the current run loop. Here's how I might rewrite the individual test method above to do that:
- (void)testSomeBehaviorCallingBack {
NSDate *fiveSecondsFromNow = [NSDate dateWithTimeIntervalSinceNow:5.0];
[_object doSomethingThatShouldCallBack];
[[NSRunLoop currentRunLoop] runUntilDate:fiveSecondsFromNow];
STAssertTrue(_callbackInvoked,
#"Delegate should send -something:delegateInvoked:");
}
This will spin the current run loop in the default mode for 5 seconds, under the assumption that -doSomethingThatShouldCallBack will schedule its work on the main run loop in the default mode. This is usually OK because APIs that work this way often let you specify a run loop to use as well as a mode to run in. If you can do that, then you can use -[NSRunLoop runMode:beforeDate:] to run the run loop in just that mode instead, making it more likely that the work you're expecting to be done will be.
Please, review Unit Testing Asynchronous Network Access. I think can help you.
In short what it does is:
Add the following method which will take care of the synchronization
between the unit test code and the asynchronous code under test:
- (BOOL)waitForCompletion:(NSTimeInterval)timeoutSecs {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if([timeoutDate timeIntervalSinceNow] < 0.0)
break;
} while (!done);
return done;
}