STOP PRESS OK before you see the word retainCount
in the following question, please skip to the EDIT at the bottom where I have stated that I have stopped using it.
My Cocoa App, which uses MRR, creates many global resources, which I am loading in main()
, before NSApplicationMain()
is called. As NSApplicationMain()
doesn't return, I have hooked the clean-up of these resources using atexit()
, like this:
atexit(cleanup);
if (![CocoaUtil initCocoaUtil] ||
![PreferenceController initPreferenceController] ||
![ResourceManager initResourceManager])
{
criticalAlertPanel(@"Failed to initialize application",
@"Failed to initialize application");
return 4;
}
retval = NSApplicationMain(argc, (const char **)argv);
However cleanup()
is getting called before any of the views in my NSDocument
subclass are dealloc
'd (I have lack of log message to show this) and hence the reference counts of the objects in the global resources is sometimes > 1
. I am being over-cautious and attempting to pre-empt memory leaks by using this method to release my global resources:
+ (void)fullRelease:(id)obj
format:(NSString *)format, ...
{
if (obj == nil)
return;
NSUInteger retainCount = [obj retainCount];
if (retainCount > 1)
{
va_list va;
va_start(va, format);
NSString *objDesc = [[NSString alloc] initWithFormat:format arguments:va];
logwrn(@"%@ has a reference count of %lu", objDesc, retainCount);
[objDesc release];
}
while (retainCount > 0)
{
[obj release];
retainCount--;
}
}
My log shows the following:
12:15:04.954 INF -[AppController applicationDidFinishLaunching:] Application launched
12:15:06.702 INF -[AppController applicationShouldTerminate:] Application terminating
12:15:06.703 INF -[AppController applicationWillTerminate:] Application terminating
12:15:06.705 DBG cleanup Cleaning-up
12:15:06.705 INF +[ResourceManager finiResourceManager] Cleaning up
12:15:06.709 WRN +[CocoaUtil fullRelease:format:] _images[2] has a reference count of 2
12:15:06.709 WRN +[CocoaUtil fullRelease:format:] _images[3] has a reference count of 2
12:15:06.709 WRN +[CocoaUtil fullRelease:format:] _images[4] has a reference count of 2
12:15:06.710 WRN +[CocoaUtil fullRelease:format:] _images[5] has a reference count of 2
12:15:06.710 WRN +[CocoaUtil fullRelease:format:] _images[6] has a reference count of 2
12:15:06.710 WRN +[CocoaUtil fullRelease:format:] _images[7] has a reference count of 2
12:15:06.711 WRN +[CocoaUtil fullRelease:format:] _images[8] has a reference count of 2
12:15:06.711 WRN +[CocoaUtil fullRelease:format:] _images[9] has a reference count of 2
12:15:06.721 DBG +[PreferenceController finiPreferenceController] Cleaning up
12:15:06.721 DBG +[CocoaUtil finiCocoaUtil] Cleaning up
My question (finally!) is:
Is there a way to ensure I clean-up my global resource after all the NSDocument
instances have been destroyed and stop getting these false negatives?
EDIT: I have unhooked the fullRelease
call and just performed a normal release
on my resources and Instruments did not detect any memory leaks, so things are OK, but I am curious as to why the NSDocument
objects don't appear to be being released before atexit()
is called.
Do not release something you do not own!
Every retain belongs to somebody else. Only send release
to an object to balance your calls to new
, alloc
, copy
, or retain
(NARC.) This sort of behaviour will inevitably cause a crash in production code.
It looks like you want to make sure an object is deallocated rather than simply taken care of. If you own the object, release it once; the other references to it belong to other objects. It's possible you do have memory leaks in your code (we can't tell just from this code sample) but those can usually be found with the Static Analyzer, Instruments, and a bit of elbow grease.
More importantly: the operating system will free all your memory for you when your process exits. This is not part of the C standard but it is simply how OS X and iOS operate, and it is the expected behaviour on other platforms that support Objective-C. So you don't have to do anything special to clean up when your process exits, except perhaps writing files to disk or similar. In fact, many Cocoa applications don't bother to release anything owned by their app delegates, because it's faster to let the operating system dump the memory than it is to call -release
on thousands of objects.
Do not call -retainCount
!
It lies. Plain and simple. It includes temporary references used by Cocoa, most importantly, and you should never attempt to interfere with those. -retainCount
is a poisoned symbol.