Reputation: 4361
In Apple's MVCNetworking sample code, the NetworkManager
class includes this method to maintain a run loop in a secondary thread dedicated to network activity (in order to run NSURLConnection
asynchronously):
- (void)networkRunLoopThreadEntry
{
while(YES) {
NSAutoreleasePool *pool;
pool = [[NSAutorelease alloc] init];
[[NSRunLoop currentRunLoop] run];
[pool drain];
}
}
Since the run
method exits immediately if there is no source attached to the run loop, this looks like an infinite while
loop which is going uselessly to consume CPU resources if there is currently no NSURLConnection attached to the run loop.
On the other hand, to keep the run loop active, some suggests to schedule an empty port in the run loop:
- (void)networkRunLoopThreadEntry
{
NSAutoreleasePool *pool = [[NSAutorelease alloc] init];
NSPort *port = [NSPort port];
[[NSRunLoop currentRunLoop] addPort:port forMode:NSRunLoopCommonModes];
[NSRunLoop run];
[pool drain];
}
However, in this case, my worry is that the run
method will never exit, which means the pool will never get drained, which means all objects allocated and autoreleased in the secondary thread will leak.
What is the way to go then?
(For the context, as many others, I'm trying to encapsulate an asynchronous NSURLConnection
inside a NSOperation
, which means it can be triggered outside of the main thread. Also, the MVCNetworking sample code, as well as the WWDC 2010 sessions Network Apps for iPhone OS, seem to suggest it is a good idea to have a unique secondary thread dedicated to network transfers to prevent latency on the main thread.)
Upvotes: 4
Views: 2570
Reputation: 385650
You can create a CFRunLoopObserver
for the kCFRunLoopBeforeWaiting
activity and add it to the run loop. In the observer's callout, release the old pool and create a new one. Untested example:
static void resetPoolCallout(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
NSAutoreleasePool **poolPointer = (NSAutoreleasePool **)info;
[*poolPointer release];
*poolPointer = [[NSAutoreleasePool alloc] init];
}
- (void)networkRunLoopThreadEntry {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSPort *port = [NSPort port];
[[NSRunLoop currentRunLoop] addPort:port forMode:NSRunLoopCommonModes];
CFRunLoopObserverContext observerContext = {
.version = 0,
.info = (void*)&pool,
.retain = NULL,
.release = NULL,
.copyDescription = NULL
};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting,
true, 0, resetPoolCallout, &observerContext);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
[[NSRunLoop currentRunLoop] run];
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
CFRelease(observer);
[pool release];
}
Upvotes: 6