In the developer documentation, it says:
If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should periodically drain and create autorelease pools (like the Application Kit does on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If, however, your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.
I was wondering what the best way to do this is. I have several methods I think would work, but don't know which is the "best". I currently have a method that start the thread and keeps it waiting for operations to perform:
- (void)startThread
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
accessoryRunLoop = [NSRunLoop currentRunLoop];
//Add a dummy port to avoid exiting the thread due to no ports being found
[accessoryRunLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoo开发者_如何学运维pMode];
while(accessoryThreadIsRunning)
{
//Keep the thread running until accessoryTheadIsRunning == NO
[accessoryRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
[pool release];
}
My options I can think of are:
1) Add a counter in the while(accessoryThreadIsRunning) so that every 50 or 100 times it will drain the autorelease pool and create a new one.
2) Every time I perform a method in that thread (using performSelector: onThread:), I can create an autorelease pool and then release it at the end of the method.
3) Make a timer so that a pool is drained and then created periodically.
I think that option 1 is the best, but would like to know if there is a different way I should be doing this. Thanks!
I'd start with dead simple and just create/drain the pool on every pass through the loop.
If it shows up during performance analysis as a bottle neck, fix it.
Keep it simple until analysis indicates complexity is required.
I just re-read your question and realized something entirely boneheaded in my answer. If you are running a run-loop it should be managing an autorelease pool automatically for you; it should create a pool at the top of the loop and drain it at the end of each pass through the loop.
You only need to cycle one yourself if you have other stuff going on outside of the runloop. Is that the case?
In any case, yes, the pattern is:
while(...) {
... create pool ...
... do stuff ...
... drain pool ...
}
Drain it each time. As others have said draining an autorelease pool is cheap.
Moreover NOT draining it can be very costly. If you have enough stuff in your autorelease pool to cause paging you cause disk I/O, and disk I/O is literally thousands if not millions of times more costly then running a linked list calling release on stuff. (and on systems like iOS that don't have paging, lots of extra objects waiting to autorelease can cause memory low warnings, which might cause applications to be forced to exit, or the foreground application to go release a bunch of Nib views or something, then it then has to recreate later...or it might just force your application to exit).
Even if you don't use "enough" extra memory to cause low memory warnings or paging you will be running a larger staler list of items to drain. More memory accesses will be between your newest autorelease item and the oldest. There is a much greater chance that the oldest autorelease item is now farther away in the memory hierarchy, so your release may have cache misses vs. a L1 or L2 cache hit. So maybe 100 times more costly. Plus the memory you would have released (and might have been hot in the cache) might well have been reused by another object.
So doing the autorelease every 50 to 100 times might not even manage to be premature optimization.
Do one release per loop, and then if that shows as a bottleneck make it every X times, and make sure that makes it faster not slower.
The main thread's run loop drains its pool on each pass, so it makes sense to do it on other threads too. If you choose to drain the pool only occasionally, you risk having a lot of autoreleased objects waiting to be deallocated for a long time. In fact, it depends on how much memory you can release on each pass of the run loop and how often you trigger the run loop. I always prefer to drain it on each pass just because it's easy and helps me keep the memory footprint as low as possible.
The conventional way is, yes to keep a counter and drain every 50 or so times, but as bbum said, just start out with draining the pool every loop, and go from there. OR you could -init
the objects that you need, and not create any autoreleased objects. (just stick clear of factory methods) Remember to -release
all your objects, though.
精彩评论