Hi All,
as a not very experienced objective-c developer (on iphone) I have a general problem with "autorelease" variables.
Is there anyway to get rid of a variable that already had already received a "autorelease" message?
As a simple scenario I have a regularly fired piece of code that just formats the current time as a string and shows it in UILabel. Using NSDate/NSDateFormater would create a couple of "autorelease" vars.
My understanding here is that these vars would never be released till the assigned autorelease-pool is releases. So normally when the application finishes. I would assume then that the pool managed memory is increasing as more as longer the app is running.
I know that recreating a new autorelease-pool might solve the issue. But is this really the desired way to do it; so re-creating an autorelease pool in a method to get rid of all the autorelease objects within the method? I would assume that re-creating a pool is a rather expensive operation.
Maybe I did not already understood the way NSAutoreleasePool is working, but I assume drain/release is only called once during the live-time of the pool. Or is there any additional "magic" (e.g. the drain method is automatically called in the application-event loop) ?
Finally, is the following code-snipple a developer might is opposed to do to reduce the memory preasure :
Code:
// calling a method that returns an "autorelease" object
NSString* aString = [SomeClass stringWithData:@"xyz"];
// ... once being ready with the aString
[globalPool drain];
appreciate any help on this. Please no references to generals hint aka "read the manual" I already did this, but could not find anything yet ...
My understanding here is that these vars would never be released till the assigned autorelease-pool is releases. So normally when the application finishes. I would assume then that the pool managed memory is increasing as more as longer the app is running.
The default auto-release pool is drained every time through the run loop, not once per application lifecycle. So as soon as your current stack of method calls completes, and you're back waiting for a UI event, the pool is drained.
If you have some specific piece of code in one of you object methods that creates a LOT of auto-released objects, then it might be worth setting up an auto-release pool in that method, so it can be drained immediately. Otherwise, this really shouldn't be a problem.
Is there anyway to get rid of a variable that already had already received a "autorelease" message?
Well... technically, if you have a variable that has been sent 'autorelease', yet still has a retain count of 1, you can send it a 'release' and it will be released right then and there. BUT YOU DON'T EVER WANT TO DO THIS, because your app will crash when the autorelease pool is released.
Quote:
Originally Posted by hcarsten
As a simple scenario I have a regularly fired piece of code that just formats the current time as a string and shows it in UILabel. Using NSDate/NSDateFormater would create a couple of "autorelease" vars.
My understanding here is that these vars would never be released till the assigned autorelease-pool is releases. So normally when the application finishes. I would assume then that the pool managed memory is increasing as more as longer the app is running.
As Joe pointed out, your understanding about when variables in the autorelease pool are released is slightly incorrect. Most applications create an autorelease pool in their 'main' function, but that pool is simply the first in a stack of pools that exist during execution of your application. The operating system creates autorelease pools before dispatching events to your application, and those pools are drained when your code returns control the the OS. So any autorelease variable you create in a single "message cycle" are cleaned up some time before the next cycle.
Quote:
Originally Posted by hcarsten
I know that recreating a new autorelease-pool might solve the issue. But is this really the desired way to do it; so re-creating an autorelease pool in a method to get rid of all the autorelease objects within the method? I would assume that re-creating a pool is a rather expensive operation.
If you have code that generates a significant number of autoreleased objects, then creating your own autorelease pool is a good solution. BTW, you're not "recreating" an existing autorelease pool, just pushing your pool onto the stack of pools such that it becomes the container for objects to be autoreleased. As soon as you 'drain' your pool, it sends 'release' to all objects in the pool and removes itself from the pool stack. AFAIK, there is no noticeable overhead in creating your own autorelease pool.
Quote:
Originally Posted by hcarsten
Maybe I did not already understood the way NSAutoreleasePool is working, but I assume drain/release is only called once during the live-time of the pool. Or is there any additional "magic" (e.g. the drain method is automatically called in the application-event loop) ?
That's correct. Once a pool is 'drained', it is removed from the pool stack and released itself.
Quote:
Originally Posted by hcarsten
Finally, is the following code-snipple a developer might is opposed to do to reduce the memory preasure :
Code:
// calling a method that returns an "autorelease" object
NSString* aString = [SomeClass stringWithData:@"xyz"];
// ... once being ready with the aString
[globalPool drain];
No, you don't ever want to do that, but if you've read all of my earlier responses, you probably know that by now. Any autorelease pool should only be drained once, and from the same context in which it was created.
thanks for this deep explanation.
It really makes sense to have the application loop draining the pool in every idle state; in fact I was hoping something like this.
I am wondering though, if there is a way to safely "force" the draining of main's autorelease pool without returning to the main run loop? I'd like to send the drain message from my mainViewController object...
What I have going is during regression testing I hang in a very tight loop exercising basically all of my methods with randomly varied entry conditions -- which means that I essentially never get back to the main run loop AND my autorelease pool doesn't drain. The issue arises because instance variables created with convenience methods don't drain either and gradually accumulate as crud and eventually it goes boom.
It's not a show stopper as it's a regression test thing only, but I'd really like to let it run for days on end while the confuser isn't doing anything else important.
__________________
"Hardware will break. Software comes broken" Unknown Calc-12E <-- ditch your old calculator. CPR•Choking <-- Review your training, just in case. (Free) All of Nature NW <-- scrolls in x and y, with pinch zoom AND scrollable text to boot. It can be done ('taint easy tho)
I am wondering though, if there is a way to safely "force" the draining of main's autorelease pool without returning to the main run loop? I'd like to send the drain message from my mainViewController object...
What I have going is during regression testing I hang in a very tight loop exercising basically all of my methods with randomly varied entry conditions -- which means that I essentially never get back to the main run loop AND my autorelease pool doesn't drain. The issue arises because instance variables created with convenience methods don't drain either and gradually accumulate as crud and eventually it goes boom.
It's not a show stopper as it's a regression test thing only, but I'd really like to let it run for days on end while the confuser isn't doing anything else important.
I don't believe there is any way to manipulate the autorelease pool that the system has created, but it may not matter as you can create and release your own autorelease pool in your testing loop.
sorry to hijack the thread (it was "stopping", and I want to "start"!)
sleep() doesn't work.
K: I guess I don't know how to separate one pool from another, since all the methods being exercised belong to the mainViewController and use the main pool. Being a lousy programmer, some of my stuff is subclassed but the engine is one "big" class. Are you suggesting I create a pool for my engine?
TIA
__________________
"Hardware will break. Software comes broken" Unknown Calc-12E <-- ditch your old calculator. CPR•Choking <-- Review your training, just in case. (Free) All of Nature NW <-- scrolls in x and y, with pinch zoom AND scrollable text to boot. It can be done ('taint easy tho)
I am wondering though, if there is a way to safely "force" the draining of main's autorelease pool without returning to the main run loop? I'd like to send the drain message from my mainViewController object...
What I have going is during regression testing I hang in a very tight loop exercising basically all of my methods with randomly varied entry conditions -- which means that I essentially never get back to the main run loop AND my autorelease pool doesn't drain. The issue arises because instance variables created with convenience methods don't drain either and gradually accumulate as crud and eventually it goes boom.
It's not a show stopper as it's a regression test thing only, but I'd really like to let it run for days on end while the confuser isn't doing anything else important.
If you've got a special testing function that loops and calls your methods, you can instantiate your own autorelease pool in the loop. Which would ensure it gets drained once per loop.
sorry to hijack the thread (it was "stopping", and I want to "start"!)
sleep() doesn't work.
K: I guess I don't know how to separate one pool from another, since all the methods being exercised belong to the mainViewController and use the main pool. Being a lousy programmer, some of my stuff is subclassed but the engine is one "big" class. Are you suggesting I create a pool for my engine?
TIA
Pools are stacked, and depend on the execution path. I suggest creating a pool in whatever code you're using to do your regression testing. Not in the actual production code.
How are you actually doing the testing? What's your test "driver"?
sorry to hijack the thread (it was "stopping", and I want to "start"!)
sleep() doesn't work.
K: I guess I don't know how to separate one pool from another, since all the methods being exercised belong to the mainViewController and use the main pool. Being a lousy programmer, some of my stuff is subclassed but the engine is one "big" class. Are you suggesting I create a pool for my engine?
TIA
After running into this problem my solution is something like this :
If you have a "loop" in a separate thread or even the main-thread you can just move the "loop body" to a separate method (instanceMethod: ). Then do
as such you will do an "indirect" method call that will go though the main-autoreleasepool "draining". Anyway you will have a small performance degree by the indirection but also by the "draining" itself. Here the operation will NOT run in the background but within the main-loop. So the "indirect" call will return when call all needed "dealloc"-operations are performed.
In case you have a separate thread you can also create your own autorelease-pool and "drain" it e.g. within the loop. Never try to run a "drain" in a separate background thread, but I assume that would not be a good idea.
The driver is just another method in the computational engine, one that generates inputs and loops through the various method calls. The keystroke sequence that launches the regression is ifdef'd out for the production build but the idea is to really whack the engine as if the "user" were banging away on the keyboard putting in all kinds of numbers that sometimes make sense and often are not the kind of thing I'd expect them to enter.
And the purpose of the test is two fold: First to make sure nothing changes computationally either because of my fumble fingers or because the OS did something to the math libraries or the hardware ALU is different between devices. Believe it or not, with loan calculations there are sensitivities to how the ALU does the math that can affect its ability to solve a problem. The second is to make sure that the execution time through the computational algorithms doesn't change drastically. On one of my production builds I left an NSLog in by accident and killed my computational performance so I don't want to do that again!
Back to the hijacked subject though. My regression testing is run via a single method in my production code, which in turn calls all of the rest of the stuff. I can build an autorelease pool inside my regression method and thus the convenience-created-autorelease-variables will be swimming in my pool and not the main pool. So then I can safely drain my pool at will? This is a question, I don't know the answer...
__________________
"Hardware will break. Software comes broken" Unknown Calc-12E <-- ditch your old calculator. CPR•Choking <-- Review your training, just in case. (Free) All of Nature NW <-- scrolls in x and y, with pinch zoom AND scrollable text to boot. It can be done ('taint easy tho)
After running into this problem my solution is something like this :
[snip]
Carsten
Thanks Carsten, I'll give that a whirl -- it's not too hard and I'll report back.
__________________
"Hardware will break. Software comes broken" Unknown Calc-12E <-- ditch your old calculator. CPR•Choking <-- Review your training, just in case. (Free) All of Nature NW <-- scrolls in x and y, with pinch zoom AND scrollable text to boot. It can be done ('taint easy tho)
...I can build an autorelease pool inside my regression method and thus the convenience-created-autorelease-variables will be swimming in my pool and not the main pool. So then I can safely drain my pool at will? This is a question, I don't know the answer...
Yes, this is exactly what I (and some others) are suggesting. And it's really simple to do this with the following code:
Code:
// assume this is your main testing loop
for ( .... )
{
NSAutoreleasePool * myPool = [[NSAutoreleasePool alloc] init];
// do some stuff that creates autoreleased variables
[myPool release];
}
It's my calculator app...
I can build an autorelease pool inside my regression method and thus the convenience-created-autorelease-variables will be swimming in my pool and not the main pool. So then I can safely drain my pool at will? This is a question, I don't know the answer...
Yes, that would be the way. Just one thing to add. Looking to the docs and users here the basic flawor is something like :
doing an autorelease to any NSObject inheriting object will add the ref. to the last created autorelease-pool. This is conceptional right; basically the pools are managed in a "stack" thereself. So an object-ref is added to top pool on this stack. Once you do a "release" of a pool. It will remove it self from this stack (of course draining all managed objects first).
One thing to add it that EACH thread will have such a pool-stack. Such whenever creating a separate thread. The "pool management" is handled dedicated to the thread.
You can of course create as many pools (and let them stacked) as you like but always be sure to "release" it once you are done. As leaking a pool will in fact also leak any object-refs.
Anyway also the pool release will definetly has a performance impact and it is in fact rather unpredictable.
My personal way is basically to let the UI as most respondible as possible and so try to delegate as much as possible to the main-thread in regards to the autorelease pool. Although I am not sure here how internally the draining is work it might give a chance to postpone the draining to "UI idle" times.
Believe it or not, with loan calculations there are sensitivities to how the ALU does the math that can affect its ability to solve a problem. The second is to make sure that the execution time through the computational algorithms doesn't change drastically.